Skip to content

Conversation

@ascheman
Copy link
Contributor

Add full JPMS support (Java 9+) while maintaining Java 8 compatibility
by a Multi-Release JAR.

**Note:** This is not a complete solution as handling of JDK 8
compatibility is outstanding. Currently, the fix would break
usage of JDK 8.

Enable `exec:java`-goal to execute Java applications using the Java
Platform Module System (JPMS) with proper ServiceLoader support.

Changes:
- Split execution logic into classpath and module-path modes
- Detect module syntax (module/class) in mainClass parameter
- Create ModuleLayer with resolveAndBind() to include service providers
- Use ModuleLayer.Controller to open packages for reflective access
- Set thread context classloader to module's classloader

The plugin now properly handles:
- Module-path execution when `mainClass` uses "module/class" syntax
- ServiceLoader provider discovery and binding in modular applications
- Reflective access to main methods in unexported packages
- Mixed module and classpath dependencies

Integration test mexec-mojohausgh-426 validates the ServiceLoader functionality
with a multi-module JPMS application (contract, provider, consumer).

Technical implementation:
- Use Configuration.resolveAndBind() instead of resolve() to include
  service providers declared with "uses" in module-info
- Obtain ModuleLayer.Controller to programmatically open packages via
  addOpens() for reflective main method invocation
- Load classes through the module layer's ClassLoader to maintain
  proper module isolation and visibility

The fix was created in the course of support-and-care/maven-support-and-care#138.
This commit fixes two issues that caused build failures in GitHub:

1. JSR-512 Main Method Detection

Problem: Tests failed with error:
"The specified mainClass doesn't contain a main method with
appropriate signature"

JSR-512 (Java 21+) allows flexible main methods that can be
non-public and instance methods. The original code used
getMethod() which only finds PUBLIC methods, causing it to
fail when searching for package-private main methods.

Solution:
- Changed from getMethod() to getDeclaredMethod() for JSR-512
- Added setAccessible(true) for non-public method invocation
- Created findMethod() helper that searches class hierarchy
- Validates return type is void (JSR-512 requirement)
- Maintains correct priority order:
  1. static void main(String[]) - traditional
  2. static void main() - JSR-512 static no-args
  3. void main(String[]) - JSR-512 instance with args
  4. void main() - JSR-512 instance no-args

2. SystemExitException Logging

Problem: Integration tests expected [ERROR] log prefix for
SystemExitException but it was missing.

When SystemExitException was thrown during method invocation,
it got wrapped in InvocationTargetException and bypassed the
logging code.

Solution:
- Added special handling in InvocationTargetException catch
- Detects SystemExitException in the cause chain
- Logs with appropriate level (ERROR for non-zero exit codes)
- Re-throws the exception to maintain expected behavior
Copy link
Member

@olamy olamy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@olamy olamy merged commit 9d265a2 into mojohaus:master Oct 19, 2025
26 checks passed
@slawekjaranowski slawekjaranowski added this to the 3.6.2 milestone Oct 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants