Example example_addReads_addExports_reflection

Part of the full Java 9 Jigsaw modules example suite.

Authors

Migrated for Java Modules support documentation of Apache MavenTM in the course of the Maven Support & Care program by Gerd Aschemann (and other team members) as forked repository. Please add discussions, requirements, bugfixes, etc. to the fork instead of the original.

What is this example about?

Modules in this example

  • modb, modmain

  • modmain has a Main class which is started in run.sh

Module Dependency Graph, created with DepVis

Example’s Module Dependency Graph

Example shows …​

  • addReads via reflection

modmain/Main adds a read relationship to modb dynamically via reflection

  • addExports via reflection

modb/pkgbinternal is also exported to modmain via reflection. For that, modmain/Main asks the (exported!) pkgb.BHelper to do this addExports. Adding cannot be done in modmain/Main directly, as the reflection method is caller-sensitive!

Note that for successful compilation, modmain/Main needs access to the exported BHelper. Therefore the option --add-exports is needed. Same thing during runtime.

Don’t try this at home

This example demonstrates an anti-pattern. Using --add-reads and --add-exports in production/main code is strongly discouraged.

These flags should only be used for testing scenarios where you need test-only dependencies (like JUnit) without polluting production module descriptors.

For a realistic example of proper usage, see Issue #5: Realistic testing example.

Better alternatives: Declare proper requires and exports in your module-info.java files instead.

Output

This example uses golden master testing to ensure output consistency. The expected output is compared with actual output using verify.sh.

Expected Output

Main: add reads of modmain to modb
BExportHelper: add exports of pkgbinternal to modmain
Main: pkgmain.Main, id=ID_Main_34, InternalB: from InternalB

Actual Output

Main: add reads of modmain to modb
BExportHelper: add exports of pkgbinternal to modmain
Main: pkgmain.Main, id=ID_Main_34, InternalB: from InternalB

Maven 4 Output

Main: add reads of modmain to modb
BExportHelper: add exports of pkgbinternal to modmain
Main: pkgmain.Main, id=ID_Main_34, InternalB: from InternalB

Maven 4 Migration

This example demonstrates module system boundary overrides using both compile-time flags and runtime reflection APIs.

Maven 4 Compiler Changes

The Challenge

This example combines static module system overrides (compile-time) with dynamic overrides (runtime reflection). At compile time, modmain needs access to modb, but without explicit requires declarations, the compiler will reject the code.

At runtime, the example uses reflection APIs to dynamically add additional module reads and exports, demonstrating programmatic module boundary manipulation.

The Solution

Configure compile-time access using compiler arguments:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>4.0.0-beta-3</version>
  <configuration>
    <compilerArgs>
      <!-- Required for compile-time access from modmain to modb -->
      <arg>--add-reads</arg>
      <arg>modmain=modb</arg>
      <arg>--add-exports</arg>
      <arg>modb/pkgb=modmain</arg>
    </compilerArgs>
  </configuration>
</plugin>

The runtime configuration provides partial access via command-line flags:

java --module-path mlib \
     --add-modules modb \
     --add-exports modb/pkgb=modmain \
     --module modmain/pkgmain.Main

Additional access is granted at runtime through reflection APIs in the code itself, as demonstrated in Main.java.

Key Takeaways

  • Compile-time flags establish basic module access for code compilation

  • Runtime flags can be split between command-line arguments and reflection APIs

  • Reflection APIs (Module.addReads(), Module.addExports()) enable dynamic module boundary changes

  • This hybrid approach is useful when module access needs vary based on runtime conditions

  • The example demonstrates that not all access needs to be configured via command-line flags