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.
Related
I am calling non-modularised code from within a module, by treating the non-modularised as an automatic module. The non-modularised code uses classes from java.sql package. I'm using Open JDK 11.0.8. At runtime I get the following exception:
java.lang.ClassNotFoundException: java.sql.Connection
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
I can resolve this by specifying --add-modules java.sql but why do I need to? I don't have this problem with other packages, for example java.beans.PropertyChangeEvent which is in the java.desktop module resolves OK without having to explicitly add that module. Are there other system modules which are not resolved by default?
I have looked at the dependency graph in the JavaDoc for the java.se module, as suggested on a previous question, and java.sql should apparently be there.
To reproduce (this sample code shows the same kind of error as I'm encountering, but the class that cannot be loaded will be SQLException rather than Connection):
For the non-modular code, compile this class and package it into a jar called nomodule-withsql-1.0-SNAPSHOT.jar:
package pack1;
import java.sql.SQLException;
public class NeedsJavaSql
{
public static void m1() {
SQLException e = new SQLException("Whatever");
e.printStackTrace();
}
}
For the module, here is the module-info:
module foo1.project {
exports foo1;
requires nomodule.withsql;
}
Here is the calling class:
package foo1;
public class Hello {
public static void main(String... args) {
pack1.NeedsJavaSql.m1();
}
}
The answer is that although the java.base module, which is available by default, does not require any other module, it does bind to services provided by other modules, so that makes them available by default, and then any other modules they require are in turn made available, and an automatic module can use any of them. So if you look at the dependency graph for the java.base module it is completely empty, but actually in practice it drags in 37 modules, including java.desktop.
However, java.sql is not one of those 37 modules.
Brief description
In a netbeans project, if I tell Netbeans that a specific jar file is to be made available on the compile-time classpath as a library, then, I understand that Netbeans would also make that jar file available on the run-time classpath. But this classpath lib "propagation" seems to work only in a non-modular project, and doesn't seem to be happening in the case of a Modular project (in Netbeans 11).
My experiment
I've created 3 simple, experimental projects, called ProjectA, ProjectB, ProjectC, in Netbeans 11. The IDE, as well as the 3 projects, are configured to run on Open Jdk12.
ProjectA
ProjectA is non-modular, and just defines pkg1.MyClassA
ProjectB
ProjectB is also non-modular, and defines pkg2.MyClassB which imports and extends pkg1.MyClassA. In ProjectB's Properties==>Libraries==>Compile, I have specified ProjectA/dist/ProjectA.jar file as a required classpath library. In ProjectB's Run options (ProjectB Properties==>Libraries==>Run) I have not specified this jar dependence. At runtime, MyClassA will be needed, because the main() method of MyClassB instantiates MyClassB which extends MyClassA.
ProjectC
ProjectC is Modular (created with Netbeans option NewProject==>JavaWithAnd==>JavaModularProject). It defines mod.world/pkg3.MyClassC, which has a main() method that instantiates pkg1.MyClassA. In ProjectC's Properties==>Libraries==>Compile, I have specified ProjectA/dist/ProjectA.jar file as a required classpath library, just as I did for ProjectB's dependence on ProjectA.jar. Also, I did not specify any jar dependence in ProjectC's Properties==>Libraries==>Run, just as in the case of ProjectB's properties. Also, I have specified --add-reads mod.world=ALL-UNNAMED in ProjectC's compiler option as well as a runtime JVM option, to enable the explicit module of ProjectC to access the unnamed module of ProjectA.jar, at both compile-time and runtime.
The Compile on save option is turned off for all the 3 projects.
What I expected
I expected to be able to successfully compile and run all the 3 Projects.
What I actually saw
ProjectA compiled successfully.
ProjectB also compiled and ran successfully.
ProjectC compiled successfully, but failed at runtime with NoClassDefFoundError for not finding pkg1.MyClassA. This runtime error goes away if, in ProjectC's Properties==>Libraries==>Run, I specify ProjectA.jar as a required classpath library (as I had specified in ProjectC Properties==>Libraries==>Compile).
My Questions
The Netbeans dialog window at Project Properties==>Library, says
"Compile-time libraries are propagated to all library categories"
. I understand that this was the reason why I was able to successfully run ProjectB, even though I had not specified any jar-file dependency at runtime for ProjectB, upon ProjectA.jar. So, why doesn't the same propagation happen in the case of ProjectC? Is it somehow because of ProjectC being a modular application? (Even if so, why?). Or is this a bug in Netbeans 11? (I did google for this before posting here).
What if, on some occasion, I wanted to stop Netbeans from propagating a compile-time library to run-time or to test-time? Is there a way to do that? For both modular and non-modular projects?
Here's the complete code
Code for ProjectA
package pkg1;
public class MyClassA {
public MyClassA(){
System.out.println ("Inside constructor MyClassA()");
}
}
Code for ProjectB
package pkg2;
import pkg1.MyClassA;
public class MyClassB {
public MyClassB() {
new MyClassA();
System.out.println ("Inside constructor MyClassB()");
}
public static void main(String[] args) {
System.out.println ("Inside main() of MyClassB");
new MyClassB();
}
}
Code for ProjectC
Code in module-info.java
module mod.world {
exports pkg3;
}
Code in MyClassC.java
package pkg3;
import pkg1.MyClassA;
public class MyClassC {
public static void main(String[] args) {
new MyClassA();
System.out.println ("Inside main() method of MyClassC");
}
}
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/
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.
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.