Example example_automatic-module-logging

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

  • modmain

  • slf4j*.jar as automatic modules

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

Module Dependency Graph, created with DepVis

Example’s Module Dependency Graph

Example shows …​

The example illustrates the usage of automatic modules with logging. We use three logging libs as automatic modules:

  • slf4j-api-1.7.12.jar contains the logging interfaces (see directory ifamlib).

  • slf4j-jdk14-1.7.12.jar and slf4j-simple-1.7.12.jar contain implementations for slf4j (see directory amlib).

The main class writes some logging output. The run script runs the main class twice, once with slf4j-jdk14 as implementation, once with slf4j-simple-1.7.12 as implementation.

The example also shows:

  • during compile time, only the interfaces are needed

  • the mapping of names in automatic modules: The part behind the last "-" is interpreted as version and cut off. Each remaining "-" is substituted by a ".".

Output

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

Expected Output

<TIMESTAMP> pkgmain.Main main
INFO: This is Main - logging.
[main] INFO pkgmain.Main - This is Main - logging.
Error occurred during initialization of boot layer
java.lang.module.ResolutionException: ...

Actual Output

<TIMESTAMP> pkgmain.Main main
INFO: This is Main - logging.
[main] INFO pkgmain.Main - This is Main - logging.
Error occurred during initialization of boot layer
java.lang.module.ResolutionException: ...

Maven 4 Output

<TIMESTAMP> pkgmain.Main main
INFO: This is Main - logging.
[main] INFO pkgmain.Main - This is Main - logging.
Error occurred during initialization of boot layer
java.lang.module.ResolutionException: ...

Maven 4 Migration

This example required special handling for automatic module dependencies when migrating to Maven 4’s Module Source Hierarchy.

Maven 4 Compiler Changes

The Challenge

By default, Maven places all dependencies on the --class-path, but automatic modules must be on the --module-path to be recognized by the module system.

When slf4j-api-1.7.12.jar is on the classpath, the compiler cannot resolve:

requires slf4j.api;  // Module not found!

The module name slf4j.api is derived from the JAR filename when it’s treated as an automatic module on the module-path.

The Solution

The solution involves two steps:

  1. Copy dependencies to a local directory using maven-dependency-plugin:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <version>3.8.1</version>
      <executions>
        <execution>
          <id>copy-dependencies</id>
          <phase>generate-sources</phase>
          <goals>
            <goal>copy-dependencies</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.basedir}/amlib-api</outputDirectory>
          </configuration>
        </execution>
      </executions>
    </plugin>
  2. Add the directory to --module-path via compiler configuration:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>4.0.0-beta-3</version>
      <configuration>
        <compilerArgs>
          <arg>--module-path</arg>
          <arg>${project.basedir}/amlib-api</arg>
        </compilerArgs>
      </configuration>
    </plugin>

This approach ensures that slf4j-api JAR is on the module-path during compilation. It allows the JAR to be recognized as an automatic module with the derived name slf4j.api.

Key Takeaways

  • Automatic modules must be on --module-path, not --class-path

  • Maven dependency plugin can copy JARs to a local directory

  • The --module-path compiler argument can be extended via <compilerArgs>

  • Module names for automatic modules are derived from JAR filenames (version numbers stripped, hyphens converted to dots)