Skip to content

Add JPMS (Java Module System) support #271

Description

@stalep

Problem

All aesh-readline modules are unnamed modules. They use META-INF/services for ServiceLoader discovery and have no module-info.java. This means:

  • They cannot be used as named modules in modular applications
  • They appear as automatic modules on the module path (module name derived from Automatic-Module-Name manifest entry)
  • The FFM terminal provider requires --enable-native-access=ALL-UNNAMED (because the module is unnamed)

Status: Deferred

Blocked on Java 8 → 11 baseline migration. Both aesh-readline and aesh currently use maven.compiler.release=8. module-info.java requires Java 9+. The baseline bump should be coordinated across both projects (aesh-readline first, then aesh).

Analysis

Split package blocker

org.aesh.terminal.tty is a split package — classes exist in both terminal-api and terminal-tty:

terminal-api (10 API types — stay in place):
Capability, MouseEvent, MouseTracking, Point, ScreenRegion, Signal, Size, SplitScreen, StatusLine, TtyOutputMode

terminal-tty (7 implementation classes — must move):
TerminalConnection, TerminalBuilder, TerminalDevice, TerminalColorDetector, PlatformThemeDetector, DeviceBuilder, TtyDetect

JPMS forbids two named modules from containing the same package. This must be resolved before adding module-info.java.

Proposed rename: org.aesh.terminal.tty.{class}org.aesh.terminal.tty.conn.{class} for the 7 terminal-tty classes.

Impact of the rename (import counts across aesh-readline + aesh):

Class External imports Notes
TerminalConnection 21 Most disruptive — used in readline, examples, aesh
DeviceBuilder 4 Used by readline EditModeBuilder
TtyDetect 2 Used by aesh LazyTerminalShell
TerminalColorDetector 1 One example
TerminalBuilder 0 Internal to terminal-tty
TerminalDevice 0 Internal to terminal-tty
PlatformThemeDetector 0 Internal to terminal-tty

External dependency JPMS readiness

All external compile-scope dependencies have JPMS support:

Dependency JPMS mechanism Module name
Netty 4.2.x MRJAR module-info (Java 11+) io.netty.common, .transport, .handler, .codec.http
commons-logging 1.4.0 MRJAR module-info (Java 9+) org.apache.commons.logging
sshd-core 2.19.0 Automatic-Module-Name org.apache.sshd.core
sshd-netty 2.19.0 Automatic-Module-Name org.apache.sshd.netty
jackson-databind 2.22.2 MRJAR module-info (Java 9+) com.fasterxml.jackson.databind

No blockers from external dependencies.

Existing Automatic-Module-Name values

All modules already declare Automatic-Module-Name in MANIFEST.MF — these become the JPMS module names:

Module Automatic-Module-Name
terminal-detect org.aesh.terminal.detect
terminal-api org.aesh.terminal.api
readline-api org.aesh.readline.api
terminal-tty org.aesh.terminal.tty
readline org.aesh.readline
terminal-ssh org.aesh.terminal.ssh
terminal-telnet org.aesh.terminal.telnet
terminal-http org.aesh.terminal.http

Implementation plan

Phase 0: Raise baseline to Java 11

  • Change maven.compiler.release from 8 to 11 in parent pom.xml
  • Coordinate with aesh: bump aesh-readline dependency, then bump aesh to Java 11

Phase 1: Fix split package

  • Move terminal-tty's 7 classes from org.aesh.terminal.tty to org.aesh.terminal.tty.conn
  • Update all imports in terminal-tty sub-packages (impl/, provider/, split/)
  • Update imports in readline, examples, aesh project, README, project site docs
  • Consider deprecated forwarding classes in old package for one release cycle

Phase 2: Add module-info.java (bottom-up)

terminal-detect:

module org.aesh.terminal.detect {
    exports org.aesh.terminal.detect;
}

terminal-api:

module org.aesh.terminal.api {
    requires transitive org.aesh.terminal.detect;
    exports org.aesh.terminal;
    exports org.aesh.terminal.formatting;
    exports org.aesh.terminal.image;
    exports org.aesh.terminal.io;
    exports org.aesh.terminal.parser;
    exports org.aesh.terminal.provider;
    exports org.aesh.terminal.tty;
    exports org.aesh.terminal.utils;
}

readline-api:

module org.aesh.readline.api {
    requires transitive org.aesh.terminal.api;
    exports org.aesh.readline.completion;
    exports org.aesh.readline.prompt;
    exports org.aesh.readline.suggestion;
}

terminal-tty:

module org.aesh.terminal.tty {
    requires transitive org.aesh.terminal.api;
    exports org.aesh.terminal.tty.conn;
    exports org.aesh.terminal.tty.provider;
    exports org.aesh.terminal.tty.split;
    provides org.aesh.terminal.provider.TerminalProvider with
        org.aesh.terminal.tty.provider.FfmTerminalProvider,
        org.aesh.terminal.tty.provider.WinSysTerminalProvider,
        org.aesh.terminal.tty.provider.CygwinTerminalProvider,
        org.aesh.terminal.tty.provider.ExecPtyTerminalProvider;
}

Key benefit: --enable-native-access=org.aesh.terminal.tty replaces ALL-UNNAMED.

MRJAR note: module-info.java goes in src/main/java/ (compiled at release=11). The Java 22 FFM overlay does not need its own module descriptor.

readline:

module org.aesh.readline {
    requires transitive org.aesh.terminal.api;
    requires transitive org.aesh.readline.api;
    requires org.aesh.terminal.tty;
    exports org.aesh.readline;
    exports org.aesh.readline.action;
    exports org.aesh.readline.action.mappings;
    exports org.aesh.readline.alias;
    exports org.aesh.readline.editing;
    exports org.aesh.readline.fuzzy;
    exports org.aesh.readline.history;
    // internal: cursor, paste, undo, util, completion.impl
}

terminal-ssh:

module org.aesh.terminal.ssh {
    requires transitive org.aesh.terminal.api;
    requires io.netty.common;
    requires io.netty.transport;
    requires io.netty.handler;
    requires static org.apache.sshd.core;
    requires static org.apache.sshd.netty;
    exports org.aesh.terminal.ssh;
    exports org.aesh.terminal.ssh.netty;
}

terminal-telnet:

module org.aesh.terminal.telnet {
    requires transitive org.aesh.terminal.api;
    requires io.netty.common;
    requires io.netty.transport;
    requires io.netty.handler;
    exports org.aesh.terminal.telnet;
    exports org.aesh.terminal.telnet.netty;
}

terminal-http:

module org.aesh.terminal.http {
    requires transitive org.aesh.terminal.api;
    requires io.netty.common;
    requires io.netty.transport;
    requires io.netty.handler;
    requires io.netty.codec.http;
    requires com.fasterxml.jackson.databind;
    exports org.aesh.terminal.http;
    exports org.aesh.terminal.http.netty;
}

Phase 3: Build configuration

  • Remove Automatic-Module-Name from maven-jar-plugin manifest entries (redundant with module-info.java)
  • Keep META-INF/services files alongside provides clauses (classpath users need them)
  • Change Enable-Native-Access manifest entry from ALL-UNNAMED to org.aesh.terminal.tty

Phase 4: Testing

  • Verify no split package errors on module path
  • Run full test suite (Surefire runs on classpath — should pass without changes)
  • Test explicit module path usage with a small test app
  • Test --enable-native-access=org.aesh.terminal.tty
  • Test GraalVM native image
  • Test classpath-only backward compatibility

Estimated effort

~6-8 hours total, best done as a coordinated effort across aesh-readline and aesh after the Java 11 baseline migration.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions