Hi all,
I would like to start a discussion around PR #618 (feat: support Java
Platform Module System) which introduces initial JPMS (module system)
support to Fesod. Below is a structured analysis to frame the conversation
for the community.
1. Background
Fesod is currently built and consumed using the traditional Java
classpath model. While this remains widely compatible, it has several
drawbacks:
- Lack of strong encapsulation: internal implementation packages are
effectively public to reflective access.
- Classpath fragility: accidental dependency shadowing or version
conflicts are only detected at runtime (or not at all).
- Growing codebase: as Fesod expands, clearly signaling what is public
API vs internal becomes more important for backward compatibility
guarantees.
- Tooling evolution: modern Java (11–25+) ecosystems increasingly expect
proper module descriptors for advanced packaging (e.g., jlink, layered
deployments, security hardening).
The Java Platform Module System (JPMS), introduced in Java 9, offers:
- Strong encapsulation (exports only what we intend to publish)
- Reliable configuration (early detection of missing/duplicate modules)
- Service discovery via provides/uses clauses (cleaner plugin
architecture)
- Foundation for optimized distributions (custom runtime images)
2. Motivation / Intent of the PR
The PR aims to:
- Introduce module-info.java descriptors for one or more core Fesod
artifacts.
- Define explicit exported API surfaces while keeping internals hidden.
- Prepare the codebase for future refactoring (e.g., service-based
extension points).
- Avoid split packages and clarify naming conventions early.
- Preserve backward compatibility for existing classpath users (no
forced migration).
- Enable downstream adopters to place Fesod on the module path without
resorting to automatic module heuristics.
3. Scope (Initial vs Future)
Initial (this PR):
- Add module-info.java to core module(s).
- Ensure build (likely Maven) compiles with --release targeting
supported LTS JDK(s).
- Maintain classpath usability (no hard module-only constructs that
block legacy usage).
- Mark only stable, intentional APIs as exported; keep experimental or
internal packages unexported.
- Optionally begin with open module (if reflection-heavy frameworks
require deep access) and tighten later.
Planned / Future (for discussion, not all in PR #618):
- Introduce dedicated submodules (e.g., core, spi, extensions, tooling)
to reduce accidental internal coupling.
- Gradually reduce the need for deep reflection (opens) by publishing
stable service interfaces.
- Add jdeps-based verification in CI to enforce modular hygiene.
- Provide a migration guide documenting exported vs internal packages.
4. Expected Benefits
Technical:
- Strong encapsulation reduces accidental binary coupling and clarifies
API guarantees.
- Earlier failure modes for configuration errors (duplicate/conflicting
dependencies).
- Cleaner boundary for external extension via service loader patterns.
- Potential performance and security improvements (less reflective
access).
- A step toward producing minimal runtime images with jlink for
containerized deployments.
Community / Ecosystem:
- Increases confidence for library integrators and framework authors.
- Signals long-term API stewardship and intentional public surface.
- Eases adoption in modern enterprise environments standardizing on
modules.
5. Compatibility and Migration Strategy
Approach (proposed for validation):
- Keep semantic versioning discipline; adding module-info is generally
binary compatible if exports are aligned with existing public APIs.
- Avoid renaming packages unless necessary to eliminate split packages.
- If current reflective frameworks (e.g., Spring, CDI, serialization
libraries) need access to non-exported internals, temporarily use opens
(targeted, not broad).
- Provide guidance: if users previously relied on internal classes, they
should migrate to documented APIs (list to be compiled).
- Test matrix: run both classpath-based tests and module-path
integration tests across JDK 11, 17, 21 and 25.
6. Risks and Mitigations
- Risk: Accidental over-restriction breaks downstream reflection.
Mitigation: Start with open module or targeted opens; gather feedback;
tighten gradually.
- Risk: Split packages across artifacts.
Mitigation: Inventory all packages (jdeps + build plugin) before
finalizing descriptors.
- Risk: Hidden internal classes currently (mis)used by users.
Mitigation: Publish a "candidate internal" list in the discussion
thread; allow feedback window before locking.
- Risk: Increased contributor friction adding new code.
Mitigation: Add CONTRIBUTING section for “Adding or modifying exports /
services.”
7. Module Naming Proposal (for discussion)
- Module base: org.apache.fesod (root/core)
- Potential future:
- org.apache.fesod.spi (public extension points)
- org.apache.fesod.extensions.<name> (optional modules)
- org.apache.fesod.tools (if build-time or auxiliary utilities)
Naming should reflect stable boundaries and avoid premature
fragmentation.
8. Service Loader & Plugin Architecture Outlook
JPMS enables a cleaner pluggable model:
- provides org.apache.fesod.spi.SomeService with ...
- uses org.apache.fesod.spi.SomeService
This reduces custom discovery code and clarifies which extension points
are stable. Over time we can:
- Replace ad-hoc reflection or classpath scanning.
- Document official extension interfaces.
- Validate service implementations at startup (fail-fast).
9. Testing and Tooling Enhancements
Proposed additions(Still no specific solution on validation):
- jdeps validation: ensure no unintended internal package leakage.
- CI job adding --illegal-access=deny to catch reflective breakages
early.
- A smoke test launching Fesod on module path only.
- (Optional) Build profile producing a jlink image to measure size
baseline now vs future optimizations.
10. Long-Term Outlook / Strategic Benefits
- Alignment with Java LTS evolution (11 → 17 → 21 → 25).
- Foundation for security hardening (limiting deep reflection surfaces).
- Potential synergy with GraalVM native image (clearer reachability
graphs).
- Easier multi-release support if needed (module-specific evolution).
- Positioning Fesod for modular/cloud-native distributions.
11. Open Questions for the Community
Please comment on:
a. Which minimal JDK baseline should we enforce for module descriptors
(11, 17, 21 or higher)?
b. Should the initial module be open (open module ...) or do we try a
strict export set immediately?
c. Are there known downstream consumers relying on internal packages we
should list before sealing them?
d. Prioritization: single consolidated module first vs introducing spi
separation now?
e. Do we want to mandate service loader adoption in the same phase, or
defer?
f. Any build plugin preferences (e.g., maven-enforcer / moditect) to
assist consistency?
12. Proposed Next Steps
- Collect feedback on scope and naming.
- Merge PR #618 with conservative (compatibility-first) exports.
- Add documentation page: “Using Fesod on the Module Path.”
- Schedule follow-up PR(s) for service provider structure and test
hardening.
13. Call for Feedback
If you maintain an integration or extension, please test the PR branch
on the module path and report:
- Any reflection warnings
- Missing exports you believe should be public (justify use case)
- Internal APIs you relied upon (so we can evaluate transitional options)
References
PR: https://github.com/apache/fesod/pull/618
Java Module System Overview (for context):
https://openjdk.org/projects/jigsaw/
Thank you for reviewing. Looking forward to your feedback on the proposed
direction, especially around the balance between strict encapsulation and
pragmatic adoption.