Why does javac complain about named automatic-modules? - java-9

Why does the Java 9 compiler warn "requires directive for an automatic module" if module-info.java references automatic modules with "Automatic-Module-Name" set? What's the risk of such modules?
This question isn't an exact duplicate of What is an automatic module? because the latter does not address the specific reasons behind the compiler warning I have referenced (the context of the question matters). That said, it is a useful link for follow-up reading.

Quoting Remi Forax:
The main issue is that an automatic module can see classes from the classpath, but it also exports all its package so there is no encapsulation, and once you require one automatic module all automatic modules from the module path are visible.
So an automatic module is a great tool when you transitioned to the module world, but in fine, you do not want any automatic modules in you dependency graph.

Well, the problem is that automatic modules require transitively all other modules. So, by requiring it, you pollute the dependency graph with a lot of stuff and later you might get very surprising behavior.
Imagine that:
You have an automatic module com.logging which is required by your (explicit) module my.app. However you also have another automatic module (not required by my.app, just residing in the module-path) called com.dbconnectors. It will get loaded because when one automatic module is required, all other will be resolved as well. It will also be accessible to my.app because com.logging requires it transitively.
Then, when your module my.app requires com.logging your code using stuff from com.dbconnectors "magically" works, while it doesn't when you remove the requires com.logging from the module descriptor.
In general transitive dependencies should be used judiciously.

Related

Itext7 Jars not loading in properly to javafx project

I've got this dependency in my pom.xml
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.1.18</version>
<type>pom</type>
</dependency>
And in my module-info.java file I have the following
requires transitive kernel;
which produces the following warning, "Name of automatic module 'kernel' is unstable, it is derived from the module's file name."
What can I do to get rid of this warning?
I tried changing it to
requires transitive com.itextpdf.kernel;
but then I get the following error, "com.itextpdf.kernel cannot be resolved to a module"
I tried a bunch of other similar lines of code but nothing worked. What can I change the code to in order to remove the error/warning?
itext7 currently isn't modular.
You can contact the developers and ask them to make it modular.
In the meantime, complex, non-modular systems like this are difficult to use from a modular project. Even if you get that to work via the automatic module system or hacking in module info via something like moditect, it pretty much destroys any potential benefit of the project being modular, and it runs the software in a way it was never designed to work.
So, make your project non-modular:
remove the module-info.java from your project.
source the javafx modules using either:
VM arguments pointing adding them to the module path OR
from a JRE/JDK distribution that includes them, e.g. BellSoft Liberica "Full JDK" or Azul Zulu "JDK FX".
For further instructions on working with non-modular JavaFX projects, see the getting started documentation at: openjfx.io.
I am not sure what "not modular" means but how can that be true if the itext7 website gives maven dependencies to incorporate into your pom.xml?
Non-modular means that you don't define a module-info.java in your project.
Read understanding modules and the documentation I linked at openjfx.io to understand the basics of the JavaFX module system and how it can be used in a JavaFX application.
Maven modules and Java Platform modules are different things, they have the same name "module" but one is a build-time definition and the other is a runtime definition. Also, a maven dependency is just a dependency, it is not a Maven module or a Java Platform module. Though you can depend on artifacts built by a maven module and you can execute those artifacts through the Java Platform module system (if they are compatible with it).
itext7 is not built as a Java Platform module. The software has no module-info.java and it does not define an automatic module name for itself either. Given that your software is depending on non-modular software, your software should not be modular either (in my opinion).

Gradle library dependency in sub-depending module

Having hard time to describe this and I bet this is something very simple, but I just can't google sollution.
I am using many modules in my project. For simple argument let's say I have modules A and B.
B depends on A.
When I add dependency to external library (using implementation keyword) in module A to use some of it's code in the module, I cannot access library's code in project B. How can I achieve that? I would like A to be my "base" project with all dependencies in that place rather than having to repeat myself in other modules, that depend on it.
The implementation configuration means the dependencies are internal (implementation specific) for the project and should not be exposed on the compilation classpath of other dependent projects. This helps encapsulate dependencies and speeds up the build as you don't need to recompile dependent projects if you only change internal dependencies.
If you want to expose them, you need to use the api configuration instead, along with the java-library plugin.

JHipster Spring Boot modularization split package

I am trying to modularize a JHipster 5 (Spring Boot 2) application and I ran into a split package problem.
In module-info.java I have the following conflicting automatic modules:
requires problem.spring.web;
requires problem;
requires jackson.datatype.problem;
When I build the project with Maven, I get several errors due to conflicting package name org.zalando.problem like this:
error: the unnamed module reads package org.zalando.problem from both problem and jackson.datatype.problem
error: module problem.spring.web reads package org.zalando.problem from both jackson.datatype.problem and problem
I would like to know how can I solve this issue. Would I have to wait for the third party library to be modularized too? What would be a nice way to solve this conflict?
This article explains a bit on how to solve Split Packages problems. I applied it to solve the split package between jsr305 and java.xml.ws.annotation by using --patch-module argument when building, as explained here. However the project did not compile when I tried the same for these packages.
The source for this project is available on GitHub
If you want to use JARs that split a package as modules, --patch-module is the only way, but it's an arduous one. Beyond patching, you also need to craft the rest of the module graph. Say you're patching module megacorp with the content of start.up, then:
you have to make megacorp read all of start.up's dependencies with --add-reads
you have to make all modules that use start.up read megacorp with --add-reads
you have to ensure that start.up is not on the module path
This can be quite complicated, particularly if you're fighting Maven along the way. Are you sure, there is no way to simply merge the two artifacts?
If not, I'd say this project might not be ready for modularization.

Maven shade relocations across all modules?

I have a maven multimodule project that has one parent pom-project and a bunch modules. One of these modules is the "main module" that has all the libraries shaded into it. All other modules depend on that module and use the provided libraries.
The main module is a Bukkit plugin that loads the other modules as extensions. These extensions are loaded all with their own classloader, but the loaded classes are shared between the loaders to be able to depend on each other. They are also able to depend on other Bukkit plugins, as their parent classloader is Bukkit's PluginClassLoader that also shares the loaded classes between plugins to allow interaction.
That's where the problems start: Different plugins may use the same library, but the classes of that library might get loaded by different classloaders which causes LinkageErrors and other problems.
My idea to solve that problem was to relocate the libraries in the main module via maven-shade-plugin. That works as expected with libraries that are only used by the main module. However relocating libraries used by the other modules causes runtime ClassNotFoundExceptions, because the modules still search for the normal package name instead of the relocated one.
Then I tried to change the imports to the relocated packages, but my IDE (IntelliJ) doesn't find the classes.
Has anyone an idea on how to solve this relocation problem? Or maybe different approaches on the classloading issue?
5 years later in a very similar context (Bukkit -> SpongeApi) I encountered this problem again, but this time I found the (probably only satisfying) solution:
The main module had its shaded version as the main artifact, so dependents could only see relocated classes and were unaware of the original classnames. This made no difference in our case, as the main module is a provided dependency anyway, but it also prevents consumers from accidentally using relocated classes directly. IntelliJ does not care for the relocations, so it was unaware of the new relocated classes. Attaching the shaded version as a secondary artifact (shadedArtifactAttached option set to true) makes the dependencies visible to the dependents again.
The dependent modules have to apply the same relocation rule as the main module, so the plugin corrects the classnames to the ones available at runtime.
This way IntelliJ is not aware of the relocations but it also doesn't need to be aware. If necessary, the relocations can be configured in a parent pom for consistant rules across all projects.
I had almost exactly the same problem you have/had (judging from the age of this question). Although I don't have a cleaner solution for the libraries overriding other plugins' versions, I do have a workaround for IntelliJ not recognizing relocated classes.
To stop it from complaining, I added the shaded jar (with the relocations) as IntelliJ library to the target module.
You can do this like so:
Go to File > Project Structure... > Modules > (target module) > Dependencies
Select the shaded jar using Add (green +) > 1. Jars or directories....
You should now see the shaded jar in the libaries list
Although it seems to work at first glance, this solution workaround has a few caveats:
Non-relocated classes are still visible to code through Maven's module dependency and if you happen to use them, you'll only see that upon compiling with Maven. (You could remove the module dependency, but it gets readded every time you reimport your pom)
You'd have to update the jar path every time you change your project's version if you include a version number in your jar file name (Workaround: Specify a static project.build.finalName)
When you add new methods or change signatures, you need to compile the library module again. (This can be worked around by creating a separate module for shading dependencies - That would actually also resolve the file name issue)

How to Prevent Dependency checking in java compiler

I am trying to prevent dependency checking in java compiler, I use command line compilation,is there any way to tell javac compiler not to check dependency while compiling a java file ?
... is there any way to tell javac compiler not to check dependencies while compiling a java file ?
The simple answer is No.
Suppose you have some class A that wants to call some method m defined by class B. In order to successfully compile A, the compiler needs to know that B is a real class, that it defines the method m, that it has the expected number and type of arguments, what checked exceptions it throws, and what type of value it returns. Without this information about B, the compiler cannot compile A.
And this propagates to the project level. If a class in project P depends on a class in project Q, the compiler must have that class (at least) in order to compile the class in P.
In short, no such compiler option exists, and it is hard to see how it could be implemented it it did.
If you're two projects are dependent on each other then they are really one project and must be built together. If the relationship is a one-way relationship then you will still need to build the dependent project first and then have the results of the project on the classpath when building the second project.
Most IDEs have capabilities to manage this. In Eclipse you can mark that one project depends on another project and the dependent project's output files will be added to the classpath of the other. Typically all dependencies are built and packaged as jars and those jar files are placed onto the classpath when compiling parent projects.
Building code without having access to the dependencies is very difficult and not recommended. In some cases it can be possible. Eclipse has built their own incremental Java compiler so that they do not have to recompile the entire project each time a single file is modified. You can read more about it here but in order to use such a compiler you will likely have to do a lot of work.
UPDATE to reflect your new edit:
In order to build a common library that common library must not depend on any classes in your platform-specific sections. As Peter Rader has mentioned the typical way of doing that is by using interfaces. For example, your common library can have an EventListener interface which receives events. In your platform specific libraries you can implement that interface and process the events according to the specific platform. Since your common library only depends on the EventListener class and not the specific implementations it does not need those specific classes when it compiles.
If you have dependencies, they will always be checked and give warnings, but your classes will be compiled anyway.
Often frameworks offer an api.jar that contains interfaces and enums.

Resources