Unnamed module interaction with named module by ServiceLoader::load - java

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.

Related

What's the difference between the 'java.se' and 'java.base' Module in Java Platform Modules System

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.

Making Maven and module-info work together

I have a project(Java 12) with several Maven dependencies, and now I'm trying to add module-info file like
module mymodule {
requires java.net.http;
}
But if I do this all Maven dependecies (in pom.xml) become invisible for code, and compiler throws errors like java: package org.openqa.selenium.safari is not visible
(package org.openqa.selenium.safari is declared in module selenium.safari.driver, but module mymodule does not read it)
Is it possible to make them work together?
The new module info ist not congruent with the information in the pom.xml. Robert wrote a good article about the differences of both systems:
https://www.sitepoint.com/maven-cannot-generate-module-declaration/

Calling a Non Module Class from a Module Class in java 9

I am trying to call a non-module class from a module class. I have created a folder structure
moduledemo > allclasses > moduleC > packageC > MyMethods.class
is the path to my module class file
moduledemo > moduleC > packageC > MyMethods.java
and
moduledemo > nomodule > packageD > DemoNoModule.class
is the no module class that I am calling from MyMethods.java
I am able to compile the DemoNoModule file. I am able to compile MyMethods.java into allclasses folder moduleC.
When I am running MyMethods I am getting error moduleC not found. Can anyone update? I am using the following command to run
java --module-path allclasses -m moduleC/packageC.MyMethods
Both files code -> Non-Module Class
package packageD;
public class DemoNoModule {
public void showD() {
System.out.println("this is show of D in No Module");
}
}
Module class calling class
package packageC;
import packageD.*;
public class MyMethods {
public static void main(String s[]) {
DemoNoModule d=new DemoNoModule();
d.showD();
}
}
Module info in module C
module moduleC {
exports packageC;
}
On one hand, the moduleC(mind improving naming?) is a named module.
While on another, the "no module class" termed by you is nothing but as stated by Alan a class present on the classpath. The classes present on the classpath during the execution are part of an unnamed module in JPMS.
Quoting the documentation further:-
The unnamed module exports all of its packages. This enables
flexible migration... 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.
This is intentional to preserve the reliable configuration in the module system. As stated further :
If a package is defined in both a named module and the unnamed module
then the package in the unnamed module is ignored. This preserves
reliable configuration even in the face of the chaos of the class
path, ensuring that every module still reads at most one module
defining a given package.
Still, to make use of a class from the unnamed module in your named module moduleC, you can follow the suggestion of making use of the flag to add ALL-UNNAMED module to be read by modules on the module path using the
following command:
--add-reads <source-module>=<target-module> // moduleC=ALL-UNNAMED
As a special case, if the <target-module> is ALL-UNNAMED then
readability edges will be added from the source module to all present
and future unnamed modules, including that corresponding to the class
path.
PS: Do take into consideration the highlighted portion(above) of the documentation as you do so.
Also note the long-term solution would be to revise your design here, for which you can plan to move your code in the class DemoNoModule into an explicit module or package it separately to be converted into an automatic module.
Java 9 programs are supposed to be modular. That is how I understood jigsaw in JDK-9. So, IMHO, you'll have to 'wrap' your packageD in another module and in the module-info for moduleC write requires moduleD. Also moduleD should export packageD.
ALL-UNNAMED is added for backward compatibility, and I suppose it will be removed in some point of Java evolution.

java 9: In module-info.java do i have to again mention packages((as requires) that are already being imported in sources

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.

Method getModule returning unnamed module for class in named module

I just started to have a look at the Java 9 module system and I was wondering whether it is possible for a class to know in which module it is located.
Therefor I created the following module
module de.test {
exports de.test.myexport;
}
and compiled a jar file that looks like
> jar --print-module-descriptor --file=Java9Test-1.0-SNAPSHOT.jar
de.test
requires mandated java.base
exports de.test.myexport
In package de.test, I have a class called Overview where I'm calling
Module module = Overview.class.getModule();
However, the returned module object is unnamed and has no ModuleDescriptor.
Am I using getModule() correctly here, or is there any other way to load the module of a class?
I'm using JDK 9 build 120 on OS X.
All JARs on the class path (with java --class-path ...) get bundled into the same so-called unnamed module, regardless of whether they are "a real module" or "just a JAR". When you ask a class from such a JAR for its module, you get the result you describe.
Try putting the JAR on the module path (with java --module-path ...) and Class::getModule should return what you expect.

Categories

Resources