Example example_layer-modules-before-finder

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?

This example is similar to this example. It shows how a module from the boot layer can be overwritten in a child layer depending on the order of resolution.

Layers in this example

We have the boot layer as the parent layer for these three child layers:

  • foo layer #1 which contains the module modfoo which requires the modules modversion1 and modcommon

  • bar layer #1 which contains the module modbar which requires the modules modversion2 and modcommon

  • bar layer #2 which contains the module modbar which requires the modules modversion2 and modcommon

The boot layer contains these modules (from mlib/)

  • modcommon which has standard functionality used by all other modules

  • modmain which does reflective calls to modbar and modfoo

The foo layer contains these modules (from foomlib/)

  • modversion1 which produces the version string "V1"

  • modfoo which prints its id and the version from modversion1

The bar layers contains these modules (from barmlib/)

  • modcommon, a new version of modcommon found in boot layer. It prints certain texts in blue colour on ANSI terminals.

  • modversion2 which produces the version string "V2"

  • modbar which prints its id and the version from modversion2

Example shows …​

This exampe shows how modcommon is resolved differently depending on how a layer is created:

  • modmain resolves modcommon from the module path (mlib/), both end up in the boot layer on startup.

  • modfoo is placed in the newly created foo-layer together with modversion1. Its "module path" foomlib/ does not contain modcommon so it is resolved from the boot layer.

  • modbar is placed in the newly created bar-layer #1 together with modversion2. barmlib/ does contain a new version of modcommon, however, the boot layer version is found first and is thus used.

  • modbar is again played in the newly created bar-layer #2 together with modversion2. This time, we create the layer putting barmlib/ in the so-called "before"-ModuleFinder. Now the new version of modcommon is resolved instead, and we see blue output on ANSI terminals when this version of modbar is called.

Note that using automatic modules in either bar-layer #1 or #2 will lead to failure. An automatic module will try to add reads dependencies to all modules in the bar-layer and in the parent boot layer. In doing this, it tries to read from both modcommon in boot as well as modcommon in bar! This leads to a module resolution error.

Output

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

Expected Output

Infos for Layer and Module:
Layer (ModuleLayer), boot layer
Layer's parents: none, as this is the boot layer
Layer's configuration including read dependencies:
java.base@11.0.28 (Configuration) -> [], java.compiler@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.datatransfer@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.desktop@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.datatransfer@11.0.28 (Configuration), java.prefs@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.logging@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.management.rmi@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.management@11.0.28 (Configuration), java.naming@11.0.28 (Configuration), java.rmi@11.0.28 (Configuration)], java.management@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.naming@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.security.sasl@11.0.28 (Configuration)], java.prefs@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.rmi@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration)], java.security.jgss@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.naming@11.0.28 (Configuration)], java.security.sasl@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration)], java.smartcardio@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.xml.crypto@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.xml@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], modcommon@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)], modmain@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration)]

Create a new 'foo layer #1' as a child of the current layer...
Reflective call to pkgfoo.Foo in modfoo in 'foo layer #1'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modfoo@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration), modversion1@1.0 (Configuration)], modversion1@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgfoo.Foo, id=ID_Main_74, using automatic module version V1

Create a new 'bar layer #1' as a child of the current layer...
Reflective call to pkgbar.Bar in modbar in 'bar layer #1'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modbar@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration), modversion2@1.0 (Configuration)], modversion2@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgbar.Bar, id=ID_Main_74, using automatic module version V2

Create a new 'bar layer #2' as a child of the current layer...
Reflective call to pkgbar.Bar in modbar in 'bar layer #2'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modbar@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@2.0 (Configuration), modversion2@1.0 (Configuration)], modcommon@2.0 (Configuration) -> [java.base@11.0.28 (Configuration)], modversion2@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgbar.Bar, id=ID_Main_74, using automatic module version V2

Actual Output

Infos for Layer and Module:
Layer (ModuleLayer), boot layer
Layer's parents: none, as this is the boot layer
Layer's configuration including read dependencies:
java.base@11.0.28 (Configuration) -> [], java.compiler@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.datatransfer@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.desktop@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.datatransfer@11.0.28 (Configuration), java.prefs@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.logging@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.management.rmi@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.management@11.0.28 (Configuration), java.naming@11.0.28 (Configuration), java.rmi@11.0.28 (Configuration)], java.management@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.naming@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.security.sasl@11.0.28 (Configuration)], java.prefs@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.rmi@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration)], java.security.jgss@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.naming@11.0.28 (Configuration)], java.security.sasl@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration)], java.smartcardio@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.xml.crypto@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.xml@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], modcommon@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)], modmain@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration)]

Create a new 'foo layer #1' as a child of the current layer...
Reflective call to pkgfoo.Foo in modfoo in 'foo layer #1'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modfoo@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration), modversion1@1.0 (Configuration)], modversion1@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgfoo.Foo, id=ID_Main_74, using automatic module version V1

Create a new 'bar layer #1' as a child of the current layer...
Reflective call to pkgbar.Bar in modbar in 'bar layer #1'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modbar@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration), modversion2@1.0 (Configuration)], modversion2@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgbar.Bar, id=ID_Main_74, using automatic module version V2

Create a new 'bar layer #2' as a child of the current layer...
Reflective call to pkgbar.Bar in modbar in 'bar layer #2'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modbar@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@2.0 (Configuration), modversion2@1.0 (Configuration)], modcommon@2.0 (Configuration) -> [java.base@11.0.28 (Configuration)], modversion2@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgbar.Bar, id=ID_Main_74, using automatic module version V2

Maven 4 Output

Infos for Layer and Module:
Layer (ModuleLayer), boot layer
Layer's parents: none, as this is the boot layer
Layer's configuration including read dependencies:
java.base@11.0.28 (Configuration) -> [], java.compiler@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.datatransfer@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.desktop@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.datatransfer@11.0.28 (Configuration), java.prefs@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.logging@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.management.rmi@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.management@11.0.28 (Configuration), java.naming@11.0.28 (Configuration), java.rmi@11.0.28 (Configuration)], java.management@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.naming@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.security.sasl@11.0.28 (Configuration)], java.prefs@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.rmi@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration)], java.security.jgss@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.naming@11.0.28 (Configuration)], java.security.sasl@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration)], java.smartcardio@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], java.xml.crypto@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration), java.logging@11.0.28 (Configuration), java.xml@11.0.28 (Configuration)], java.xml@11.0.28 (Configuration) -> [java.base@11.0.28 (Configuration)], modcommon@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)], modmain@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration)]

Create a new 'foo layer #1' as a child of the current layer...
Reflective call to pkgfoo.Foo in modfoo in 'foo layer #1'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modfoo@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration), modversion1@1.0 (Configuration)], modversion1@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgfoo.Foo, id=ID_Main_74, using automatic module version V1

Create a new 'bar layer #1' as a child of the current layer...
Reflective call to pkgbar.Bar in modbar in 'bar layer #1'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modbar@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@1.0 (Configuration), modversion2@1.0 (Configuration)], modversion2@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgbar.Bar, id=ID_Main_74, using automatic module version V2

Create a new 'bar layer #2' as a child of the current layer...
Reflective call to pkgbar.Bar in modbar in 'bar layer #2'...
Infos for Layer and Module:
Layer (ModuleLayer), not boot layer
Layer's parents: Parent is boot layer
Layer's configuration including read dependencies:
modbar@1.0 (Configuration) -> [java.base@11.0.28 (Configuration), modcommon@2.0 (Configuration), modversion2@1.0 (Configuration)], modcommon@2.0 (Configuration) -> [java.base@11.0.28 (Configuration)], modversion2@1.0 (Configuration) -> [java.base@11.0.28 (Configuration)]
pkgbar.Bar, id=ID_Main_74, using automatic module version V2

Maven 4 Migration

This example required a special migration approach due to its unique requirement of compiling multiple versions of the same module.

Standard Migration

The primary modules (modbar, modcommon v1.0, modfoo, modmain, modversion1, modversion2) were migrated using the standard Module Source Hierarchy approach documented in the central Maven 4 Migration guide.

Special Handling: Multiple Module Versions

This example demonstrates module layer resolution by using two different versions of modcommon:

  1. modcommon v1.0 (from src/modcommon) - used in the boot layer

  2. modcommon v2.0 (from src2/modcommon) - used in bar layer #2 to demonstrate version overriding

    Problem

    Maven 4’s Module Source Hierarchy cannot handle multiple versions of the same module in a single build. The <sources> element expects one source location per module name.

    Solution

    Hybrid compilation approach in m4/compile.sh:

  3. Maven compiles all primary modules (modbar, modcommon v1.0, modfoo, modmain, modversion1, modversion2) using Module Source Hierarchy

    • Maven creates JARs in target/ using maven-jar-plugin with classifiers

  4. The compile script copies layer-specific JARs to their respective directories for dynamic module loading:

    # Copy foo layer modules (modfoo, modversion1) to foomlib/
    for mod in modfoo modversion1; do
        cp "target/example_layer-modules-module-resolution-m4-1.0-${mod}.jar" "foomlib/${mod}.jar"
    done
    
    # Copy bar layer modules (modbar, modversion2) to barmlib/
    for mod in modbar modversion2; do
        cp "target/example_layer-modules-module-resolution-m4-1.0-${mod}.jar" "barmlib/${mod}.jar"
    done
  5. Manual javac invocation compiles modcommon v2.0 from src2/:

    javac -d mods2 --release 11 --module-version=2.0 \
          --module-path target --module-source-path ../src2 \
          $(find ../src2/modcommon -name "*.java")
    
    jar --create --file=../barmlib/modcommon.jar -C modcommon .
  6. JAR placement for dynamic module loading:

    • target/ - Maven-built JARs (with classifiers)

    • mlib/ - Boot layer modules (OS-specific: Unix uses symlink to target/, Windows uses directory with copied JARs)

    • foomlib/ - foo layer modules (modfoo, modversion1) copied from target/

    • barmlib/ - bar layer modules (modbar, modversion2) copied from target/, plus modcommon v2.0 compiled manually

      The run.sh script creates mlib using OS-specific approaches to avoid AccessDeniedException when Java’s ModuleLayer API traverses symlinks on Windows.

This hybrid approach is necessary because:

  • Maven 4’s Module Source Hierarchy is designed for single-version module builds

  • The example’s educational value depends on demonstrating module version resolution in layers

  • Manual compilation of the second version preserves the example’s behavior while leveraging Maven 4 for the primary build