How split packages are avoided in Java 9 - java

I am new to Java 9 and was going though the modular video lectures by Java on YouTube.
They mentioned 3 benefits of modularization-
1. No missing dependencies
2. No cyclic dependnpcies
3. No split packages.
As far as I understand about split packages is that let's say an application is dependant on multiple dependncies and let's say package abc.pqr.xyz is present in more that 1 jar.
Then there is a chance that some of the classes in that package will be used from jar1 while other classes from jar2.
This might lead to some problems at runtime which will be hard to debug.
Video says modularization solves this issue.
But how that's what I am trying to understand?
Let's say there is test.module1 which has below module info -
module test.module1{
exports abc.pqr.xyz;
}
Another module2 with below module info-
module test.module2{
exports abc.pqr.xyz;
}
Now let's say in my application I added dependencies of both of these modules-
module test.myapp{
requires test.module1;
requires test.module2;
}
Now again I have 2 modular dependencies where there is a chance that some of the classes will be present in both of these modules.
So at runtime how it will be resolved from which module to pick up the class definitions?
How Java 9 will avoid split packages problem?

With the scenario described in the question, you'll start facing an error reading :
Module test.myapp reads package from both test.module1 and test.module2
Readability of the Modules from The State of the Module System elaborates over the use of Modules as follows and shall interest your use-case(emphasis mine):
The readability relationships defined in a module graph are the basis
of reliable configuration: The module system ensures
that every dependence is fulfilled by precisely one other module
that the module graph is acyclic
that every module reads at most one module defining a given package
and that modules defining identically-named packages do not interfere with each other.
the benefit of implying the same in the module system is detailed as well
Reliable configuration is not just more reliable; it can also be
faster. When code in a module refers to a type in a package then that
package is guaranteed to be defined either in that module or in
precisely one of the modules read by that module.
When looking for the
definition of a specific type there is, therefore, no need to search
for it in multiple modules or, worse, along the entire class path.
That said, the current solution to your implementation is
if the modules test.module1 and test.module2 are explicit modules, you can choose to implement the package abc.pqr.xyz in either one of them
OR you pull it out from both into a separate module test.mergeModule of your own which can thereafter be used as an independent module across its clients.
if these(or any one of them) are automatic modules, you can make use of the bridge extended to the classpath and let such jar remain on the classpath and be treated as the unnamed module, which shall by default export all of its packages. At the same time, any automatic module while reading every other named module is also made to read the unnamed module.
Quoting the document again and to illustrate with an example, so that you can correlate to your question :
If code in the explicit module com.foo.app refers to a public type in
com.foo.bar, e.g., and the signature of that type refers to a type in
one of the JAR files still on the class path, then the code in
com.foo.app will not be able to access that type since com.foo.app
cannot depend upon the unnamed module.
This can be remedied by treating com.foo.app as an automatic module temporarily, so that its
code can access types from the class path, until such time as the
relevant JAR file on the class path can be treated as an automatic
module or converted into an explicit module.

The Java module system resolves the split package problem by rejecting this sort of scenario at JVM startup time. When the JVM starts it will immediately begin resolving the module graph, and when it encounters two modules in test.myapp's module path, the JVM will throw an error indicating test.module1 and test.module2 are attempting to export the same package.

Related

Use java serviceloader build with jdk8 use in >= java9

I have two jars. One provides a service interfaces and a service loading class and one provides the implementation of that service.
This works perfectly when running this in jdk8, but I get an service type not accessible to unnamed module #3754a4bf error when running on jdk9 or higher.
I migrated that two jars to a module base jar and that works great when running on >= jdk9, but that fails on jdk8 of cause, because of wrong class file version.
So, I don't have problem using java serivceloader api with java 8 or 9.
I'm aware of https://blog.codefx.org/tools/multi-release-jars-multiple-java-versions/ , but I want to avoid to make the build process more complicated. The build already involves class relocation and
other stuff.
My question: Is there a way to use the same jars running on jdk8 and >=jdk9 using java serviceloading api?
In JDK9 and up, all jars are effectively 'modules'. If they do not have a module definition (which you create by making a file named module-info.java and putting a module declaration inside), its name is 'unnamed module #whatever', it exports all its packages, and can access anything exported by any other module (in module-speak: it 'reads' everything). Meaning, if all your classpath dependencies are such unnamed modules, they all export everything and they all read everything thus they all can access anything marked public from anybody else – which is how it worked in JDK8, and thus, why it's all compatible.
To be clear: in JDK9, for code in module (jar) A to access a method, class, or field from module (jar) B, then in addition to the usual access modifiers (the public keyword), B needs to 'export' that package, and A needs to read that module, or it doesn't work. If you aren't explicitly writing module info files for either side, well, then you get the default behaviour which is 'export everything' and 'read everything', bringing us back to the JDK8 scenario of: the thing has to be marked public, and then you can access it.
I actually do not recommend you make that module-info file; that's a big step, one you should only take once you're familiar with the module system.
The error means that the 'service type'[1] is not 'accessible'[2] to 'unnamed module #3754a4bf'[3]. Let's break that into pieces:
[1] serviceloader works by defining an interface that a 'service provider' implements, and the class using the service loader then ends up with a bunch of instances of that interface; each representing one implementation. It's the Foo in ServiceLoader.load(Foo.class).
[2] 'accessible' is module speak for: Either the code that needs this did not explicitly 'read' it, or the code that has this did not export it.
[3] 'unnamed module#3754abf' is the name of the module that is trying to access it.
Taking this all together, it means: You're operating under false assumptions: Whatever jar contains your service interface (that Foo I was talking about), is NOT an unnamed module, or, possibly, isn't public. Note that if it is a public interface inside a not-public class (i.e.: /* package private */ class Example { public interface MyServiceInterface {} }), that probably still counts as 'not public enough'.
If it's a named module (which means: it has a module-info.java file), then export the package that the service interface is in. See any jigsaw (the name of the java module system) tutorial on how to set that up. If it's not, ensure it's a public interface or abstract class, and if its inside some other type, make sure those are public too. If neither is the case, check your classpaths; javac is rather adamant that one of these two is the case.

How to get Eclipse to stop asking to create a module-info java file on new Java project creation?

Everytime I try to create a new java project Eclipse keeps asking if I want to add a module-info java file to the source folder. It's getting pretty annoying as there's no immediately obvious option to opt out of this check.
IDE for Java Developers, Photon release 4.8.0
See while creating a new project, after you click>> next on the very first dialog "new java project." There is one another dialog box pops up when you click >> finish. It will lead you to the 3rd dialog box which asks for the creation of module-info java file?? & gives you two option create & don't create.
You should select "don't create."
Here are some advantages of the file
module-info.java contents:
To declare a jar file as a named module, one needs to provide a module-info.class file, which is, naturally, compiled from a module-info.java file. It declares the dependencies within the module system and allows the compiler and the runtime to police the boundaries/access violations between the modules in your application. Let’s look at the file syntax and the keywords you can use.
Module module.name – declares a module called module.name.
Requires module.name – specifies that our module depends on the module module.name, allows this module to access public types exported in the target module.
Requires transitive module.name – any modules that depend on this module automatically depend on module.name.
Exports pkg.name says that our module exports public members in package pkg.name for every module requiring this one.
Exports pkg.name to module.name the same as above, but limits which modules can use the public members from the package pkg.name.
Uses class.name makes the current module a consumer for service class.name.
Provides class.name with class.name.impl registers class.name.impl class a service that provides an implementation of the class.name service.
opens pkg.name allows other modules to use reflection to access the private members of package pkg.name.
Opens pkg.name to module.name does the same, but limits which modules can have reflection access to the private members in the pkg.name.
One great thing about the module-info.java syntax is that the modern IDEs would fully support your efforts of writing them. Perhaps all of them would work beautifully. I know that IntelliJ IDEA does content assist, quick fixes of the module files when you import classes from the module you haven’t required yet, and so on. I don’t doubt Eclipse IDE and NetBeans IDE offer the same.
Perhaps this is not a perfect solution, but it will stop asking if you choose to use Java version 8 compiler (JavaSE-1.8). If you need any newer Java version, I'm affraid don't have an answer.

java jigsaw extend jar library

I am trying to separate a project of mine into Jigsaw Modules. One pain point has been to use .jar libraries in multiple modules and extend their functionality in some of them.
To be more concrete, I am using minimal-json-0.9.1.jar via "requires transitive minimal.json" in a module-info.java file and I set the module to be on the modulepath in eclipse.
I then created this interface:
import com.eclipsesource.json.JsonObject;
public interface JsonSerializable {
JsonObject toJson();
}
Note that JsonObject comes from the minimal.json library.
Now I get a warning message in eclipse: "The type JsonObject is not exported from this module". How can I get rid of it?
Suggestion: Do not transitively rely on an automatic-module and change your module-descriptor to make use of
requires minimal.json;
Explanation: While including the transitive modifier, an implied readability is expressed by the requires clause. This means that any module that depends upon your module [requires your.module] will also be able to read the minimal.json module.
The warning from eclipse seems to be highlighting the same point that the use of JsonObject in any further module that requires your module would not be because your module have explicitly exported [exports some.package;] the package including the class, rather because of the transitive dependency.

How to export all packages from Java 9 module? [duplicate]

This question already has an answer here:
Why is exporting the entire module not allowed?
(1 answer)
Closed 5 years ago.
Right now, for every module I have, I need to explicitly specify packages I want to export. For example:
module core {
exports cc.blynk.server.core;
exports cc.blynk.server.core.protocol.handlers.decoders;
exports cc.blynk.server.core.protocol.handlers.encoders;
}
However, it is not very convenient. I would like to do something like that:
module core {
exports cc.blynk.server.core.*;
}
Is there any way to do that? Where this limitation comes from?
No, you can not use a wildcard to export all packages within the module. You will have to export each package explicitly.
It is not allowed since this could majorly lead to conflicts in the different packages exported from different modules which defies the purpose of modularising the code.
Additionally quoting from one of the threads:
The packages exported by a module are meant to be a stable API that
consumers can rely on. For this reason, we make the module author
spell out the exported packages explicitly. This also dials down the
likelihood of multiple modules needlessly exporting the same package.
Additionally, it avoids the confusion that would occur if com.abs.*
was exported without qualification while com.abs.foo was exported with
qualification.

Why Java 9 does not simply turn all JARs on the class path into automatic modules?

In order to understand the categories we have:
platform explicit modules
application explicit modules
open modules
automatic modules
unnamed module
All classes and jars within the classpath will be part of the unnamed module. But why is that what we need? Where is the advantage over automatic modules? I could "require" those damn legacy jars to make them to an automatic module. Do I not have included everything with it?
There are at least two reasons:
Just as regular modules, automatic ones are suspect to certain examinations by the module system, e.g. not splitting packages. Since JARs on the class path can (and occasionally do) split packages, imposing that check on them would be backwards-incompatible and break a number of applications.
The unnamed module can read all platform modules, whereas automatic modules can only read those that made it into the module graph. That means a JAR needing the java.desktop module (for example) will work from the class path but not from the module graph unless java.desktop also makes it into the graph (via a dependency or --add-modules).
I have no time right now to check the second but that's what the State of the Module system says:
After a module graph is resolved, therefore, an automatic module is made to read every other named module, whether automatic or explicit
Resolution works on the declared dependencies and an automatic modules declares none.
Apart from the items listed in the accepted answer, there is one more difference: unnamed modules can access all packages of modules that come with Java, even is they are not exported.
As long the class is public, access will work - the same as before Java 9. But as soon as a jar is run from module path, it will be able to access only exported packages.
For example if some .jar has this code:
com.sun.jmx.remote.internal.ArrayQueue c = new com.sun.jmx.remote.internal.ArrayQueue(10);
it will run normally without any warnings when placed on class path, but when run from module path (as automatic module) it will fail at runtime:
Exception in thread "main" java.lang.IllegalAccessError: class test1.C
(in module test1) cannot access class com.sun.jmx.remote.internal.ArrayQueue
(in module java.management) because module java.management does not export
com.sun.jmx.remote.internal to module test1
Note that this is different from the well known illegal reflective access warning, which is about using reflection to access private fields or methods. Here we are statically (non-reflectively) accessing public class (but from non-exported package).

Categories