eclipse-jdt / eclipse-jdt/eclipse.jdt.ui
Package required libraries into generated JAR should respect modules
- Dominant language
- Java
- Stars
- 59
- Forks
- 127
- Avg merge
- 23h 30m
- Merged PRs (30d)
- 35
Description
When exporting a modularized Java application as a runnable JAR file using the "Package required libraries into generated JAR" option and running the resulting JAR, the classes of the application and its libraries cannot make use of JPMS modules.
### Steps to reproduce
- Create a Java project with a `module-info.java` library class (in this case `LibClass`)
- Export that project as a JAR - Alternatively, any other modular JAR can be used.
- Create another Java project with a `module-info.java`
- Add the JAR from before to the build path
- Add a `requires` declaration to the `module-info.java` referencing the library project
- Add a main class containing `System.out.println(ExportTest.class.getModule());` and ``System.out.println(ExportTest.class.getModule());``
- Create a Java application run configuration for that main class
- Right click on the project > Export > Java > Runnable JAR file
- Make sure that "Package required libraries into generated JAR" is selected
- The output when running the JAR with `java -jar` is different to the output when running it from the console

```java
module test {
requires my.lib;
}
```
```java
package abc;
import io.github.danthe1st.mavenpackaging.lib.LibClass;
public class ExportTest {
public static void main(String[] args) {
System.out.println(ExportTest.class.getModule());
System.out.println(LibClass.class.getModule());
}
}
```
### Expected output
When running it from the console, I get the following output:
```
module test
module my.lib
```
### Actual output
When running the generated runnable JAR using `java -jar`, both classes are in the unnamed module:
```
$ java -jar myjar.jar
unnamed module @119d7047
unnamed module @119d7047
```
When running it with `-p` and `-m`, it fails to start because the library module cannot be found:
```
$ java -p myjar.jar -m test/org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
Error occurred during initialization of boot layer
java.lang.module.FindException: Module packaging.lib not found, required by test
```
When performing the same experiment without using any library (hence no `requires` statement and no `System.out.println(LibClass.class.getModule());`, I am getting the following output (correct):
```
$ java -p Untitled.jar -m test/org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
module test
```
### My suggestion
I would suggest to make the following changes:
1. Change the [jar in jar loader](https://github.com/eclipse-jdt/eclipse.jdt.ui/tree/master/org.eclipse.jdt.ui/jar%20in%20jar%20loader/org/eclipse/jdt/internal/jarinjarloader) to add module information to all modules to all JARs containing a `module-info.class`.
- The nested JARs should be part of the same `ModuleLayer`.
2. If the packaged _application_ (not libraries) has a `module-info.class`, package the application as a JAR within the JAR as if it was a library and add it to the `Rsrc-Main-Class`. In that case, do not copy the application classes (and especially not the `module-info.jar`) into the final JAR directly.
- That way, both the application and libraries are packaged as JARs and loaded as modules (assuming the first change) that can reference each other
- This could be made optional using a switch.
3. If the exported application depends on another _project_ and that project has a `module-info.jar` as well, that library project should (ideally) be converted to a JAR file as well. Currently, exporting something like that would copy the `module-info.java` files.
- For discussion: Should this be done for dependencies/libraries without a `module-info.java` as well?
These 3 suggestions can be implemented independently but the last two only make sense with the first one (and it would make sense to test at least the first two together).
The first suggestion on its own would be an improvement already as it allows properly exporting applications where only some libraries are modularized (for example JavaFX which requires (it's possible otherwise but not supported) the JavaFX runtime to be on the modulepath). It _could_ also help other applications that might use the jar in jar loader for packaging (I don't know whether many exist and I don't think any are modularized)
Note: I don't know whether JavaFX would still work that way due to its use of native libraries but it's just the example that came to my mind.
However, one limitation worth mentioning is that logic for doing this would have to use JDK 9+ classes.
### Why I think this might be important enough to investigate
With time, modules are used more and more. Using nested JARs is practically the only way to export modularized application into a single JAR file while preserving the modularity of the application. Packaging modular applications (e.g. JavaFX applications) without using jlink/jpackage which exports a Java runtime and creates platform dependent artifacts for end users is currently a pain point when using Java.
Modules are here to stay and Java tooling should properly support them where possible (and now there is work on [multi-release JARs etc](https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4275) so I think this might be a good point in time to also consider that).
Eclipse is the only IDE that provides a way to export applications like that. It would be nice if it also preserved (JPMS) modularity.
Contributor guide
Research direction
Start with the jar in jar loader under org.eclipse.jdt.ui/jar in jar loader/org/eclipse/jdt/internal/jarinjarloader and reproduce the modular runnable-JAR export using the provided module-info.java examples. Compare java -jar with the -p and -m commands, then determine how module-info.class files and nested JARs are handled. Done means the exported JAR preserves the expected named modules for the application and modular libraries.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100