Java 9, Jigsaw and automatic modules - java

Java 9 / 10. I have been struggling with a simple project for more than a week.
As you can see in the picture, I want to use commons-collections as an automatic module (I have tried to add it with maven but that did not work out well).
So, I have red that I need to put the jar onto the module-path. Where does IntelliJ take this modulepath from? How can I tell the IDE to add commons-collections into the project so that
1. the compiler can find it at compile time and
2. Maven can find it at build time?
Anyone can help?
EDIT:
I have tried to add it in the project-structure dialog as a module dependency in all kinds of different combinations. I have literally tried hundreds of things, moved the jar around in the structure and I cannot find a simple enough doc to tell me how to do this.
I have used compiler options to add "--module-path automatic" (module specific and general compile options) in order to make IDEA find the thing and let Java make an automatic module out of it.

You need to add a library entry first, to make it available under Modules:
Step 1: Add a library (Add -> Java -> jar file)
Step 2: Select the module (remember to click "Apply")
After that, the module-info.java file will be successfully validated:

Intellij uses a module path if you run a program from an (intellij) module containing module-info.java, otherwise it will use a classpath.
I tried importing common4 as a module, it does seem to work for me, but I had to use a different 'requires' argument as compared to yours. Your 'requires' is 'commons.collections4', mine is 'org.apache.commons.collections4' (check the commons4 manifest entry for the highlighted Automatic-Module-Name and use that instead).
If the Automatic-Module-Name is missing from the commons4 manifest (it is absent from version 4.1 and earlier), Java may not detect the jar as a module if the name contains digits or illegal characters. Some maven repository jars therefore will not work and intelli will not see those jars as modules.
You can also check for a bad filename by using the following command:
jar --file=/path/to/jar --describe-module
If the command fails, it's likely that the jar does not have an Automatic-Module-Name entry and that the filename is poorly named.
ok
jar --file=C:\temp\jigsaw1-1.0.jar --describe-module
bad
jar --file=C:\temp\jigsaw1.0.jar --describe-module`
'jigsaw1.0: Invalid module name: '0' is not a Java identifier'
Some maven jars may therefore fail to be detected as modules as they tend to look similar.

Related

Unable to launch a JavaFX application which uses Apache POI

I have a JavaFX application that works as expected. I need to use Apache POI to read and write excel files. The following are the steps I have taken:
Added the required dependency
implementation 'org.apache.poi:poi-ooxml:5.2.3'
Added the module to module-info.java
requires org.apache.poi.ooxml;
Tried to use the library within a function:
#FXML
private void downloadTemplate() {
XSSFWorkbook workbook = new XSSFWorkbook();
}
All this is fine with no issues. However when I try to run the application, I get the following two errors (interchanging)
> Task :Start.main() FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module SparseBitSet not found, required by org.apache.poi.ooxml
and
> Task :Start.main() FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module commons.math3 not found, required by org.apache.poi.ooxml
I can however, clearly see both libraries under 'external libraries'
I am using IntelliJ Community Edition 2022.1.2 and running the project using Java 17.0.1. Any help would be highly appreciated.
SparseBitSet is an automatic module, it has no module-info of its own (probably commons-math3 is as well), and is without an Automatic-Module-Name entry in its manifest.
Gradle puts libraries without a module-info.class or an Automatic-Module-Name in their manifest on the class path, not the module path, so they won't be treated as modules, and the module finder won't find them.
You can:
hack the gradle build to allow the modules to be found. (I don't use Gradle so I have no specific advice on how to do that other than referring to the documentation).
Hack the library jar which you want to be treated as a module to include a module-info.class or an Automatic-Module-Name in its manifest.
Or, switch to maven, which automatically places automatic modules on the module path.
The easiest way to do this, IMO, is to create a new JavaFX project in Idea, then add the required dependencies as maven dependencies and add your code.
Or, as swpalmer suggests in the comments, request that library maintainers update their codebase to make their libraries modular.
And, when you run your app, make sure all jars are on the module path, not the class path.
Or, make your app non-modular by removing the module-info.java from it, then manually place the JavaFX modules on the module-path and add them with the --add-modules switch.
FAQ
Are you SURE that automatic modules are put on the class path by Gradle?
From the Gradle documentation section Building Modules for the Java Module System:
To tell the Java compiler that a Jar is a module, as opposed to a
traditional Java library, Gradle needs to place it on the so called
module path. It is an alternative to the classpath, which is the
traditional way to tell the compiler about compiled dependencies.
Gradle will automatically put a Jar of your dependencies on the module
path, instead of the classpath, if these three things are true:
java.modularity.inferModulePath is not turned off
We are actually building a module (as opposed to a traditional
library) which we expressed by adding the module-info.java file.
(Another option is to add the Automatic-Module-Name Jar manifest
attribute as described further down.)
The Jar our module depends on is itself a module, which Gradles
decides based on the presence of a module-info.class — the compiled
version of the module descriptor — in the Jar. (Or, alternatively, the
presence of an Automatic-Module-Name attribute the Jar manifest)
It is the third point that is key. Java can treat a library with no module-info.class and no Automatic-Module-Name in the Jar manifest as an automatic module if it is on the module path. However, Gradle will by default, only place libraries which fulfill one of those two conditions on the module path.
Using jewelsea's answer above, I have been able to solve the problem. I am posting the answer here to help anyone else who encounters the problem in future.
So, the overall problem is, as said in the answer above, both SparseBitSet and commons-math3 are automatic modules with no module-info of their own. The solution that worked for me was to convert them into the modules expected by the project. Here are the steps I took:
Use a gradle plugin 'extra-java-module-info'. The github page didn't show how to import it to a normal gradle file so here it is:
plugins {
id 'org.gradlex.extra-java-module-info' version '1.0'
}
Note the names that your application expects for the modules. In my case, from the error messages thrown, they were 'SparseBitSet' and 'commons-math3'
Locate the said libraries on the sidebar under 'external libraries' and note the 'jar' file names. In my case, they were 'commons-math3-3.6.1.jar' and 'SparseBitSet-1.2.jar'.
Add a section 'extraJavaModuleInfo' to your gradle files and use the parameters as follows: module('jar file name', 'name expected by your project', 'jar version'), as shown in the blue rectangle in the image above.
extraJavaModuleInfo {
module('commons-math3-3.6.1.jar', 'commons.math3', '3.6.1')
module('SparseBitSet-1.2.jar', 'SparseBitSet', '1.2')
}
That's it. Try to sync and run your project. Thanks jewelsea.

maven multi-module project with two versions of protobuf

We have a multi-module maven project. One of the modules has a bunch of .proto files, which we compile to java files. Pretty much every other module depends on this module. Most of them use Protobuf 2.4, but one needs to use 2.5.
Is there any nice way to do this? (The not nice way is to edit the pom file to say "2.5", build a jar, manually copy that jar to wherever we need it, and then change the pom file back to 2.4.)
Never used protobuf, but, as I understand it's a plugin that generate stuff.
So I'm gonna give you generic pointer hoping it will help.
I think you should either try to make 2 jar with different classifier from a single module, see https://maven.apache.org/plugins/maven-jar-plugin/examples/attached-jar.html
For example classifier proto2.4 and proto2.5
then you can add the classifier when you define the dependency to that module.
Other option I see is having 2 modules, the real one, you have now, and another one for 2.5
Generate a zip from the main one and the second module would be empty but have a dependency on the generated zip, unzip it and then compile with the plugin config for 2.5
Slower at execution, a bit dirtier imho, but can be needed if for example you need more customization than just the version.
I finally got things to work more or less how I wanted. I created a new module with JUST a pom file; that pom file refers to the proto files of the original module, but compiles them with the proto2.5 compiler and puts the result into its own directory.

Specific question concerning "The package org.w3c.dom is accessible from more than one module: <unnamed>, java.xml"

I'm in the process of migrating my companies projects from Java 1.8 to OpenJDK 14. Like multiple other people (fe. in this, this or this thread) I've run into some troubles with Java 9 Modularity, specifically with the package org.w3c.dom.
I have already generated the module-info.java and currently both the JRE System Library (OpenJDK 14) and all external JARs are on the Modulepath in the Java Build Path. So I no longer have the error module: <unnamed>.
The actual compile error I get right now is:
The package org.w3c.dom is accessible from more than one module: batik.all, java.xml, xml.apis.ext
BTW I can't just simply delete the JARs batik.all and xml.apis.ext, since other packages of those are used in the project which are not in the JavaSE.
I've also been told by my supervisors to not convert any non-maven projects to maven projects, which I mention since all other references I find to this problem seem to discuss the solution of utilising Maven Dependencies and Excludes.
Now after some analysis I've come to the conclusion that the most effective way for me to get rid of these errors would be to unpack the two JAR files, decompile the packages, delete the duplicate org.w3c.dom from those external JARs and finally recompile the whole thing and pass that custom JAR to the build path.
Does that idea make sense? Would you do it this way if you couldn't use Maven dependencies?
Since we're migrating from 1.8, I've never actually used module-info.java, so perhaps there's a way simpler way of excluding those packages in that file? I'm open for alternative solutions!
Last but not least, if my described solution is the way to go, what would be the best way to unpack and repack those JAR files?
You do not need to decompile/recompile anything, you can simply delete the directory in the JAR file corresponding to the package.

IntelliJ Dependency Management with Java 9 Modules and Maven - what am I doing wrong?

I am still fairly new to Java, and am trying to restructure one of my Java projects for the Java 9 module structure, and it seems like I need to do everything completely manually. I am hoping I am simply missing some steps somewhere in the correct way to be handling my dependencies:
Old Way (Java 8 and older)
I create my project.
I add my dependencies to the pom
I code.
I compile.
New Way (modules)?
I create my project.
I create my modules.
I create a project level .pom that defines my dependencies for all of the modules
I manually add each dependency to each module it will be used in. If I need to update a dependency, I remove the old one from each module and put the new one in, after updating the Maven .pom (this means someone cloning my project from git has to figure out all the dependencies as well, since .iml files aren't just a list of dependencies and don't generally get put on git)
I manually add every possible package a module might need to the module-info file, since there is no autocomplete otherwise, once again, manually updating the list of requires statements if the dependency is changed.
I code.
I remove all of the unused packages from the module-info file.
I compile the code.
This just seems inefficient. Is there a better / correct way I should be doing this? This is the process that their documentation indicates I should be using. ( see: https://www.jetbrains.com/help/idea/creating-and-managing-modules.html#working-with-module-dependencies )
Yes, I know I don't have to add all the packages manually and can just search on the command line to figure out where a class I want to use is located, but that just takes even longer.

How to examine required libraries?

I developing a web application with a lot of libraries like, Spring, Apache CXF, Hibernate, Apache Axis, Apache Common and so one. Each of these framework comes with a lot of *.jar libraries.
For development I simple take all of the delivered libraries and add them to my classpath.
For deployment not all of these libraries are required, so is there a quick way to examine all the required libraries (*.jar) which are used by my source code?
If you move your project to use Maven such things become easier:
mvn dependency:analyze
mvn dependency:tree
For your example, Maven + IDE + nice dependency diagrams could help allot.
See an example of this : it's much easier this way to figure out what happens in a project, and this way you don't need to add to your project "all delivered libraries" - just what it's required.
JDepend traverses Java class file
directories and generates design
quality metrics for each Java package.
JDepend allows you to automatically
measure the quality of a design in
terms of its extensibility,
reusability, and maintainability to
manage package dependencies
effectively.
So, as a quick, dirty, and potentially inefficient way, you can try this in Eclipse:
Create two copies of your project.
In project copy #2 remove all the jars from the classpath.
Pick a source file that now has errors because it can't resolve a class reference. Pick one of the unresolved classes and note its fully qualified class name.
Do Control-Shift-T and locate the unresolved class. You should be able to see which jar its contained in since all the jars are still in the classpath for project copy #1.
Add the jar that contains this unresolved class back into your classpath in project copy #2, then repeat steps 3 and 4 until all class references are resolved.
Unfortunately you're not done yet since the jar files themselves may also have dependencies. Two ways to deal with this:
Go read the documentation for all the third-party packages you're using. Each package should tell you what its dependencies are.
Run your application and see if you get any ClassNotFoundExceptions. If you do, then use Control-Shift-T to figure out what jar that class comes from and add it to your classpath. Repeat until your project runs without throwing any ClassNotFoundExceptions.
The problem with #2 is that you don't really know you've resolved all the dependencies since you can't simulate every possible execution path your project might take.

Categories

Resources