This is the module declaration of the java.rmi module:
module java.rmi {
requires java.base;
requires java.logging;
exports java.rmi.activation;
exports com.sun.rmi.rmid to java.base; // <-- cycle
...
}
So, there is a cyclic dependency between java.rmi and java.base, right? Are cycles allowed between platform modules?
The module system forbids statically declaring cycles with requires clauses. This is true for platform and application modules and the example you give does not violate that rule.
Requires clauses are just one source for readability edges in the module graph, though. Others are command line flags, reflection, requires transitive, and I'm sure there are more. Adding all these may result in cycles in the module graph and that is not forbidden.
In your concrete example the cycle is only created once java.base reads java.rmi, which could happen if it uses reflection on classes in com.sun.rmi.rmid.
Related
I am not sure if I got it right: java.base is the underlying base module from all other modules and contains all the base stuff from them, like a superclass from a class. And java.se is the module which contains the whole JDK, like a subclass (which contains the basic functionality and more specific stuff)
java.base is the base module; every other module implicitly or explicitly depends on it:
If the declaration of a module does not express a dependence on the java.base module [...] then the module has an implicitly declared dependence on the java.base module.
(Java Language Specification 17 §7.7.1)
java.se "defines the API of the Java SE Platform" (per documentation). It roughly (?) comprises all the classes which were present in Java SE before modularization, except being separated now in different modules, e.g. java.desktop or java.sql. It does not include JDK specific modules (such as jdk.javadoc). A Java runtime might not necessarily provide all of these modules; for example you could use jlink to create a Java runtime which only contains the modules you need (at the minimum java.base).
The Java API specification can also be helpful for understanding the content of modules and their relations:
java.base
java.se
Your comparison with a class (module java.base) and its subclass (module java.se) works in this case because java.se acts kind of like an 'aggregator' module which itself does not contain or export any packages but only has indirect exports through requires transitive. See this question for why the java.se module exists. Although normally you would not declare a dependency on java.se because that defeats the purpose of making custom small runtime images; instead you would only declare dependencies on the specific modules you really need, e.g. java.logging.
Such aggregator modules are rather uncommon though. In most cases modules will rather be used for grouping related packages, and restricting access to internal implementations. However, in these cases services defined by one module and the corresponding service provider being implemented and declared by another module are similar to your analogy.
I'm refactoring the code so it uses modules . The problem I'm facing is, I got cyclic dependency between modules: convertersapp, validatorsapp and modelapp. There are 6 modules namely:
1. convertersapp
2. exceptionsapp
3. mainapp
4. modelapp
5. serviceapp
6. validatorapp
My current approach - module-info. java files for every modules are:
1. convertersapp: module convertersapp {
requires modelapp;
requires gson;
requires exceptionspp;
requires validatorapp;
exports converters.others to serviceapp;
exports converters.json to modelapp;
}
2. exceptionsapp: module exceptionspp {
exports exceptions to convertersapp, mainapp,serviceapp, modelapp;
}
3. mainapp: module mainapp {
requires serviceapp;
requires exceptionspp;
requires modelapp;
}
4. modelapp: module modelapp {
exports model to mainapp, validatorapp,convertersapp, serviceapp;
exports model.sorting to mainapp,serviceapp;
requires gson;
requires exceptionspp;
requires convertersapp;
}
5. servicapp: module serviceapp {
exports service to mainapp;
requires modelapp;
requires exceptionspp;
requires convertersapp;
}
6. validatorapp:
module validatorapp {
exports validator to convertersapp;
requires modelapp;
}
A cyclic dependency hierarchy should be avoided because it is difficult to understand and it is furthermore unclear in which order the modules should be build (A before B before C before A ...).
Common strategies:
Merge all three modules to one
Build a common utility module that is used by the others, but which itself does not use others.
Move some classes to avoid the cyclicity.
A corner case, nevertheless worth to look for:
Because I was lazy, I copied the module-info.java file from the 'parent' module to the child, and just added the dependency to that 'parent', ending up with a module info file in the 'child' as below:
module com.pany.parent
{
requires transitive com.pany.parent;
}
Gradle just complained about a cyclic dependency involving the module com.pany.parent …
Of course this was alone my fault, but after several hours working on the migration to Jigsaw, you sometimes don't see the forest because of so many trees …
I have 2 libraries, which I have migrated to Java 10 and used module system.
First thing which makes me worry is that I had a lot of errors like:
Error:java: the unnamed module reads package org.aspectj.internal.lang.reflect from both aspectjrt and org.aspectj.weaver. To fix that, I have added requires org.aspectj.weaver; to module info. Actually I had to put there lot of other things which I don't use, but e.g. Spring uses. Only then I was able to compile it.
So in the end, my module-info for the first library looks like this:
module my.lib1 {
requires spring.context;
requires spring.core;
requires spring.context.support;
requires spring.beans;
requires org.aspectj.weaver;
requires slf4j.api;
requires metrics.core;
requires com.fasterxml.jackson.databind;
requires java.validation;
requires org.hibernate.validator;
requires javax.el;
exports my.lib1;
}
For the second library I also had to add a lot of libs that are used by dependencies, not me:
module my.lib2 {
requires org.hibernate.orm.core;
requires java.sql;
requires java.persistence;
requires spring.context;
requires spring.tx;
requires spring.orm;
requires spring.data.jpa;
requires spring.beans;
requires HikariCP;
requires metrics.core;
requires slf4j.api;
requires spring.core;
exports my.lib2;
}
Both libs are compiling now. I put them in my local mvn repo and started third project which depends on this two.
module my.project {
requires my.lib1;
requires my.lib2;
}
And now I got the same error... Error:java: the unnamed module reads package org.aspectj.internal.lang.reflect from both aspectjrt and org.aspectj.weaver, but this time, adding requires org.aspectj.weaver; doesn't help. I have noticed when I put only one of the libs in the module (not both together, but one lib1 or lib2) it works.
Is it normal that I have to put different libs in my module-info which are used not by my but by other dependencies? (e.g. shouldn't it be Spring's responsibility to require aspectj?).
And the most important thing: how do I fix the problem with my project which depends on my two libs?
I have noticed when I put only one of the libs in the module (not both together, but one lib1 or lib2) it works.
Using the module system, a package may only be offered by a single module in your current module graph. This is one of the main reasons for the introduction of the module system: avoidance of ambiguous dependencies (as they occur on the classic class path; called split packages). And that's why it works with only one of your modules.
In your case, both modules aspectjrt and org.aspectj.weaver are offering the same package org.aspectj.internal.lang.reflect and that's where the error message comes from (the unnamed module reads package org.aspectj.internal.lang.reflect from both aspectjrt and org.aspectj.weaver).
how do I fix the problem with my project which depends on my two libs?
like Andy Guibert wrote to this topic, you could:
unsplit the package
bundle them in a single package
hope that third party modules become named modules in the future.
AspectJ has been ported to the module system. Check the newest version, if you don't use it (requires org.aspectj.runtime, requires org.aspectj.weaver)?
Additionally, here you can find some more information about the module system.
I have a project like this:
\---main
\---src
\---com.foo
\---UnnamedStart.java
\---api
\---src
\---com.foo.api
\---ApiInterface.java
\---module-info.java
\---impl
\---src
\---com.foo.impl
\---ApiInterfaceImpl.java
\---module-info.java
Implementatio of UnnamedStart.java is:
public static void main(String[] args) {
ServiceLoader<ApiInterface> services = ServiceLoader.load(ApiInterface.class);
...
}
Note that main is unnamed module.
api/src/module-info.java is:
module com.foo.api {
exports com.foo.api;
}
and impl/src/module-info.java is:
update 1.1 - code below updated see comments, added requires
update 1.2 - code below updated, provides A with B changed to provides B with A mistake during creating question, originally was ok
module com.foo.impl {
requires com.foo.api; //added (update 1.1)
provides com.foo.impl.ApiInterface
with com.foo.api.ApiInterfaceImpl; //vice versa (update 1.2)
}
When I run my code in UnnamedStart.java I end up with no element in services.
I also tried to create a static method in com.foo.api.ApiInterface:
static List<ApiInterface> getInstances() {
ServiceLoader<ApiInterface> services = ServiceLoader.load(ApiInterface.class);
List<ApiInterface> list = new ArrayList<>();
services.iterator().forEachRemaining(list::add);
return list;
}
and add in api/src/module-info.java line uses com.foo.api.ApiInterface; but it gave the same result (nothing).
The only way I made it work is by migrating main from unnamed to named module.
1. How does java 9 work when unnamed module trying to interact with named module?
2. Does it possible to make it work and keeping the main like unnamed module?
update 1.3 - added related project
ServiceLoader::load works as usual, but the are other things.
[Short answer]
1. Unnamed module reads the same like named module to named module, but named module can not access types in the unnamed module.
2. You are trying to launch an application from a non-modular JAR so you have to explicitly resolve required modules by --add-modules com.foo.impl.
Note that your required modules have to be on module graph (e.g. add by --module-path).
[More details]
1. There are 4 different types of modules: built-in platform module, named module, automatic module, unnamed module and each of them are named apart from unnamed module
As they wrote the unnamed module treats all the other modules the same like named module:
All other modules have names, of course, so we will henceforth refer to those as named modules.
The unnamed module reads every other module. [...]
The unnamed module exports all of its packages. [...] It does not, however, mean that code in a named module can access types in the unnamed module. A named module cannot, in fact, even declare a dependence upon the unnamed module.
[...]
If a package is defined in both a named module and the unnamed module then the package in the unnamed module is ignored.
Even an automatic module indeed is also named:
An automatic module is a named module that is defined implicitly, since it does not have a module declaration.
2. Second part of this answer
If you compile non-modular code or launch an application from a non-modular JAR, the module system is still in play 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. Not necessarily all of them, just those that you directly depend on (the module system will pull in transitive dependencies) - or you can use ALL-MODULE-PATH (check the linked post, it explains this in more detail).
This #nullpointer comment will be useful
Also, the module resolution still needed the impl to be resolved during the startup. To check which you could also make use of the --show-module-resolution flag.
working with java 9 modules, if i am using java.xml in my code...
1) i will import xml package using import statement...
2) if i don't mention that this package is required in the module declaration of my module...
- will the compilation of my module work.. ??
i would guess... no... and on mentioning that xml package is required on module-info.java... it might work.
so.. what I am wondering is... is that not redundancy... every importing package is implicitly... required. (unless i need to understand module even better)
Is there a way to mention that all imported packages are required in module declaration, other wise it could be a long list to mention in module-info.java?
First of all, in module-info.java you mention modules, not packages. E.g. java.xml is a module which contains about 25 packages. So, if your module uses 10 packages from the java.xml module, you don't have to repeat that 10 times in module-info.java, you write requires java.xml just once. So, that huge list of dependencies is not huge actually.
If you really want to skip all those declarations, you can just not create module-info.java (but I don't recommend to do that). A module that does not have module-info.java is called an automatic module and it implicitly requires all other modules.