Moditect
Tooling for the Java Module System
Install / Use
/learn @moditect/ModitectREADME
ModiTect - Tooling for the Java Module System
Version 1.0.0.Final - 2023-05-03
The ModiTect project aims at providing productivity tools for working with the Java module system ("Jigsaw").
Currently the following tasks are supported:
- Generating module-info.java descriptors for given artifacts (Maven dependencies or local JAR files)
- Adding module descriptors to your project's JAR as well as existing JAR files (dependencies)
- Creating module runtime images
Compared to authoring module descriptors by hand, using ModiTect saves you work by defining dependence clauses based on your project's dependencies, describing exported and opened packages with patterns (instead of listing all packages separately), auto-detecting service usages and more. You also can use ModiTect to add a module descriptor to your project JAR while staying on Java 8 with your own build.
In future versions functionality may be added to work with other tools like jmod etc. under Maven and other dependency management tools in a comfortable manner.
Usage
ModiTect's functionality is currently exclusively exposed through a Maven plug-in. The core implementation is a separate module, though, so that plug-ins for other build systems such as Gradle could be written, too.
Generating module-info.java descriptors
To create a module-info.java descriptor for a given artifact, configure the generate-module-info goal as follows:
...
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
<version>1.0.0.Final</version>
<executions>
<execution>
<id>generate-module-info</id>
<phase>generate-sources</phase>
<goals>
<goal>generate-module-info</goal>
</goals>
<configuration>
<modules>
<module>
<artifact>
<groupId>com.example</groupId>
<artifactId>example-core</artifactId>
<version>1.0.0.Final</version>
</artifact>
<additionalDependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>example-extended</artifactId>
<version>1.0.0.Final</version>
</dependency>
</additionalDependencies>
<moduleInfo>
<name>com.example.core</name>
<exports>
!com.example.core.internal*;
*;
</exports>
<requires>
static com.some.optional.dependency;
!com.excluded.dependency;
*;
</requires>
<opens>
com.example.core.internal.controller to javafx.fxml;
</opens>
<opensResources>
com.example.resource;
com.example.resource.icon;
com.example.resource.sound;
</opensResources>
<uses>
com.example.SomeService;
</uses>
<provides>
com.example.SomeService with com.example.SomeServiceImpl1,com.example.SomeServiceImpl2;
</provides>
<addServiceUses>true</addServiceUses>
</moduleInfo>
</module>
<module>
...
</module>
</modules>
<jdepsExtraArgs>
<arg>--upgrade-module-path=...</arg>
</jdepsExtraArgs>
</configuration>
</execution>
</executions>
</plugin>
...
This will generate a module descriptor at target/generated-sources/com.example.core/module-info.java.
For each module to be processed, the following configuration options exist:
artifact: The GAV coordinates of the artifact for which a descriptor should be generated (required)additionalDependencies: Additional artifacts to be processed; useful if the main artifact depends on code from another artifact but doesn't declare a dependency to that one (optional)moduleInfo: Allows fine-grained configuration of the generated module descriptor (optional); has the following sub-elements:name: Name to be used within the descriptor; if not given the name will be derived from the JAR name as per the naming rules for automatic modules (optional)open: Whether the descriptor should be an open module or not (optional, defaults tofalse)exports: List of name patterns for describing the exported packages of the module, separated by ";". Patterns can be inclusive or exclusive (starting with "!") and may contain the "*" as a wildcard. Inclusive patterns may be qualified exports ("to xyz"). For each package from the module, the given patterns are processed in the order they are given. As soon a package is matched by an inclusive pattern, the package will be added to the list of exported packages and no further patterns will be applied. As soon as a package is matched by an exclusive pattern, this package will not be added to the list of exported packages and no further patterns will be applied. (optional; the default value is "*;", i.e. all packages will be exported)opens: List of name patterns for describing the open packages of the module, separated by ";". Patterns can be inclusive or exclusive (starting with "!") and may contain the "*" as a wildcard. Inclusive patterns may be qualified exports ("to xyz"). For each package from the module, the given patterns are processed in the order they are given. As soon a package is matched by an inclusive pattern, the package will be added to the list of open packages and no further patterns will be applied. As soon as a package is matched by an exclusive pattern, this package will not be added to the list of open packages and no further patterns will be applied. (optional; the default value is "!*;", i.e. no packages will be opened)opensResources: List of package names only containing resources as png, css or mp3 files i.e. everything not being java files, separated by ";". For JavaFX 9+, it is required to force open resource-only packages. Please, refer to https://github.com/javafxports/openjdk-jfx/issues/441 for more details.opensResourcesallows to do this. Contrary toopens,opensResourcescannot accept patterns because Moditect manages patterns using a technical solution that can only works with compiled java classes. Hence, each resource-only packages must be declared one by one.requires: List of name patterns for describing the dependences of the module, based on the automatically determined dependences. Patterns are inclusive or exclusive (starting with "!") and may contain the "*" character as a wildcard. Inclusive patterns may contain thestaticandtransitivemodifiers, in which case those modifiers will override the modifiers of the automatically determined dependence. For each of the automatically determined dependences of the module, the given patterns are processed in the order they are given. As soon as a dependence is matched by a pattern, the dependence will be added to the list of dependences (if the pattern is inclusive) or the dependence will be filtered out (for exclusive patterns) and no further patterns will be applied. Usually, only a few dependences will be given explicitly in order to override their modifiers, followed by a*;pattern to add all remaining automatically determined dependences.addServiceUses: Iftrue, the given artifact will be scanned for usages ofServiceLoader#load()and if usages passing a class literal are found (load( MyService.class )), an equivalentuses()clause will be added to the generated descriptor; usages ofload()where a non-literal class object is passed, are ignored (optional, defaults tofalse)uses: List of names of used services, separated by ";" only required ifaddServiceUsescannot be used due to dynamic invocations ofServiceLoader#load(), i.e. no class literal is passed (optional)provides: List of services with their provided service implementations, separated by ";". A service and its implementation must be separated by the keyword "with" e.g.serviceX with implementationX; serviceY with implementationY;. If the module implements a particular service through several service implementations, those implementation classes must be separated by "," e.g.myService with implementation1, implementation2, implementation3;.jdepsExtraArgs: A list of arguments passed to the jdeps invocation for creating a "candidate descriptor"
It is also possible to run this goal directly, specifying the different options as JVM parameters like this:
mvn moditect:generate-module-info \
-Dm
