How does Maven work with Java 9 modules? - maven

If I have all types of modules in my project (application, automatic and unnamed) how exactly Maven will work with them?
Can I enforce Maven to treat some jars as automatic modules whereas other modules to stay in classpath. How to gradually migrate to module system with Maven?

Maven just manages your dependencies (jars). It doesn't care if dependencies are java modules or not.
The only way how Maven can help is if you launch your application through Maven (ex. mvn spring-boot:run) then you can add some JVM parameters like --add-modules.
If you are wondering about automatic and unnamed modules it's all depends how you launch your application, there are two ways how you can do that:
Launch your application from module code:
Since a module must require all of its dependencies and those can only be fulfilled by other named modules (i.e. not JARs on the class path) all dependencies of a modular JAR must be placed on the module path. Yes, even non-modular JARs, which will then get turned into automatic modules.
The interesting thing is that automatic modules can read the unnamed module, so their dependencies can go on the class path.
Launch your application from non module code:
and because non-modular code does not express any dependencies, it will not resolve modules from the module path.
So if non-modular code depends on artifacts on the module path, you need to add them manually with the --add-modules option.
For example if you want to use ServiceLoader.load(Foo.class); and you compile your application from non-modular code you'll have to add provider module of the Foo class explicity to module graph with --add-modules.
Note from The State of the Module System:
If a package is defined in both a named module and the unnamed module then the package in the unnamed module is ignored.

Related

JavaFX16: Unsupported JavaFX configuration: classes were loaded from 'unnamed module #...'

I have a Maven multi-module project on IntelliJ that produces 3 jars, 2 of which are executables and the other one is for commmon resources.
One of the executable modules has a main class that asks if the user wants to start the program with CLI or GUI.
Running this main from IntelliJ works without problem, instead when I use Maven to make the jar (using maven shade):
mvn package -pl myModule -am
and then launch the jar with GUI option, this warning appears:
com.sun.javafx.application.PlatformImpl startup
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module #...'
The GUI then starts without problems.
How can I get rid of this warning?
The JDK of my project is openJDK 16 (I've already added module-info.java in order to work with javafx), openJFX 16, maven shade plugin 3.2.4, maven compiler plugin 3.8.1.
If there's need to add furthermore information, I'll add them.
It seems that maven is shading the JavaFX jars into a single fat jar.
Shading multiple modules into the same jar is not possible, because a jar can only contain 1 module. So, I suppose the shade plugin resolves that problem by removing the module-info files of the dependencies it's using, which means the JavaFX code will not be loaded as a module, and you get a warning like this.
I think the only way to get rid of the warning is to not use shading, but keep the JavaFX modules as separate jar files, that you then put on the module path when running the application.

Does maven add all dependencies to modulepath by default?

I read on Java 9 Modularity book:
Dependencies are always put on the module path, even when dependency isn't modularized yet.
[...]
The most important changes made to Apache Maven for support of the Java module system are as follows:
Uses the modulepath during compilation
Supports a mix of explicit modules and automatic modules as dependencies
I'm looking at maven documentation and I cannot find this information anywhere.
Does maven by default add <dependencies> to the modulepath (only?) and if yes, after which maven version?
Also if the above is true is there a way to instruct maven to not use modulepath at all?
No, Maven puts dependencies to module path only for those Maven modules that have module descriptors (module-info.java). Non-modular Maven modules still put their dependencies to classpath.
You can run Maven with -X option to see exact command-line options that are passed to javac.

When Java 9 module system integrate into Maven, how to handle automatic module

I have already practiced java 9 module system integration with Maven project. and it seems to work well but I was anxious about backward compatibility of maven module compiled before java 9.
For backward compatibility, JPMS(Java Platform Module System) has Automatic Module Concept. In JPMS, Jar file without module-info.java is regarded as Automatic Module and other java module can use this jar file by using its file name as module name.
In case of integration with maven project, I think additional support for JPMS backward compatibility needed.
first, basic jar file and maven jar file is not same structure.
second, maven jar file naming is incompatible with rule can be compatibility with JPMS module naming rule. see the below example.
<artifactId>previous-version-module</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
In above cases, We already uses previous-version-module-0.0.1-SNAPSHOT and want to use this module in new java 9 maven project.
module new.module{
requires previous-version-module-0.0.1-SNAPSHOT
}
but it occur compile error or IDEA tool throw error.
I'm wondering how to use maven jar file which compiled before java 9 in java 9 and maven integration project.
I searched some additional module definition rule or maven plugin, I can't find any solution.
Thanks.
If the module name isn't obvious then you can use jar --file=<jarfile> --describe-module to see the derived name. Alternatively, use the --list-modules option, i.e. java -p <jarfile> --list-modules.
Once you know the module name then you use requires <module> and it should work. The maven-compiler-plugin puts all dependences on the module path when building a project that has a module-info.java in the source tree.

Add non-osgi jars to RCP4 project

I am building an RCP4 application.
I have two non-osgi jars called a.jar and b.jar. Both jars have tons of non-osgi dependencies. One of the dependencies of a.jar is b.jar. So the hierarchy looks like this:
My application
|--a.jar
|----aDependency1.jar
|----aDependency2.jar
|----aDependencyN.jar
|----b.jar
|------bDependency1.jar
|------bDependency2.jar
|------bDependencyN.jar
Some of the bDependencyN.jars are different versions of the aDependencyN.jars
(An example is commons-logging-1.0.4.jar vs commons-logging-1.1.2.jar)
I need to directly reference a.jar and b.jar from my RCP4 application. In other words, when I write code, I will import packages from a.jar and b.jar)
Which is the best approach:
Use bnd 2.4 via command-line to turn all non-osgi jars into osgi ones. I then add every jar to my project via target file
Create a new project "Plug-in from existing JAR archives", and select a.jar and all of its dependencies and export it as a "deployable plugin and fragment" called a.with.libs.jar. I do the same with b.jar and create b.with.libs.jar. I then add those 2 new jars to my project via target file
Create a new project "Plug-in from existing JAR archives", and select a.jar and all of its dependencies, and b.jar and all of its dependencies and export it as a "deployable plugin and fragment" called ab.with.libs.jar. I then add the new jar to my project via target file
Is there a better approach than the suggestions above?
One option is to use bnd-platform (I am also the author) to manage third party dependencies and create OSGi bundles from them. You can use it with both dependencies retrieved from Maven repositories and local Jars (see the README). When you configure a Maven dependency it will also include the transitive dependencies. Under the hood it uses bnd. If needed you can also customize how the Jars are wrapped. bnd-platform is a plugin for Gradle, you can easily start with this template - just add your dependencies and provide the configuration as described in the project README (bundle symbolic names, versions) and run gradlew bundles. The created bundles can then be added to the target platform. You can also use bnd-platform to build a p2 repository / update site.

How does plugin specific configuration gets interpreted during runtime

I have a parent POM which defines maven-compiler-plugin definition/configuration under pluginManagement.plugins.plugin.
I have a multi-module project which has a requirement to re-define the configuration for their respective modules (e.g. moduleA, moduleB). Since the definitions are inherited can I be assured that configuration specific to moduleA, moduleB will be used while performing the build.
e.g. moduleA wants to use jdk 1.5 to compile and moduleB wants to use JDK 1.6 to compile.
How should this be defined in the POM files? Do I need to define anything in the parent POM or should I just define compiler-plugin with respective configurations on moduleA, moduleB.
You can simply just define the compiler plugin with its appropriate configuration part which you need. But don't define the version of the compiler plugin this is inherited via the pluginManagement part.

Resources