I am trying to restrict access to a package in my Java application using module-info.java.
For the sake of simplicity, lets say I have the packages org.test.a, org.test.b and this module-info.class:
module test {
exports org.test.a;
}
I then compile that, install the artifact to my local repository and import it in a second project.
There, I am still able to access classes of both packages org.test.a and org.test.b, even though only org.test.a should be accessible.
Only when I modularize that project (by adding a module-info.java that requires the test module), I can no longer compile it:
java: package org.test.b is not visible
(package org.test.b is declared in module test, which does not export it)
Is it not possible to restrict package access to applications that are part of the "unnamed module", or am I just doing something wrong?
I am a bit confused, because even as part of the unnamed module, I am not able to access classes of unexported packages from the jdk (such as jdk.internal.misc.Unsafe).
Related
I have a simple Java project using the non-modularized dependency "io.prometheus:simpleclient_hotspot:0.16.0". It has been working fine until, for other reasons, I wanted to use the Java module system and add a module-info.java to my project. Once I do that, I start getting the compilation error:
error: package io.prometheus.client is not visible import io.prometheus.client.CollectorRegistry;
^ (package io.prometheus.client is declared in the unnamed module, but module simpleserver does not read it)
The prometheus client library isn't modularized, so it is called an "unnamed module". How do I get access to the packages of such a library? I assume I add a dependency in my module-info.java?
This seems like a basic, common JPMS newbie issue, but after doing lots of searches I can't find the solution to this issue.
I'm having a really hard time with some Java modules. I'm working on an old Java project which has been "migrated" to Java 11. Nowhere in the project is there any module-info.java file defined.
I'm getting compilation errors in Eclipse/VS Code, which look like:
The package org.w3c.dom is accessible from more than one module: <unnamed>, java.xml
I don't fully understand why it's causing the problem, but I added a module-info.java definition to the root of the module.
module com.company.app {
requires java.xml;
}
And that compilation error went away. I now have visibility errors everywhere and many, many more than before.
I've started to fix the visibility errors with exports and imports entries as needed, but now I have a problem.
In one of the projects, there is a source and a separate source-test folder. I've defined a module definition in the source folder.
The code in the source-test folder is separate, but has the same package structure. The following code:
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.experimental.categories.Category;
The import org cannot be resolved. (in the line of the import static).
The type org.junit.Test is not accessible (in the corresponding line)
The type org.junit.experimental.categories.Category is not accessible (once again, in the corresponding line.)
I don't want to add the junit dependency to the main project code, since it's a testing dependency. However, if I define another module-info.java module inside the source-test folder, it complains about the build path containing a duplicate entry 'module-info.java'.
How can the dependencies and modules be correctly defined?
I would like to use ResponseSubscribers.ByteArraySubscriber that is in jdk.internal.net.http. I use openjdk11.
I tried 2 things :
1/
I added to maven compiler plugin exports module
<compilerArgs>
<arg>--add-exports</arg><arg>java.net.http/jdk.internal.net.http=fr.app</arg>
</compilerArgs>
-> it compile !
2/
I created module-info.java
module fr.app {
requires java.net.http;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.datatype.jsr310;
exports fr.app;
exports fr.app.parser;
}
There is an error when running a junit test that use the class importing jdk.internal.net.http
fr.app.AppException: java.io.IOException: class fr.app.MyClass$BodySubscribers (in unnamed module #0x6537cf78) cannot access class jdk.internal.net.http.ResponseSubscribers$ByteArraySubscriber (in module java.net.http) because module java.net.http does not export jdk.internal.net.http to unnamed module #0x6537cf78
I understand BodySubscribers must be exported only in named module. But my module is name fr.app right ?
But my module is name fr.app right ?
Not really, while you've created the module-info.java in your project, during the execution of your application your actual code seems to be found on the classpath eventually.
Hence your MyClass is residing in the unnamed module and the error reads as follows
class fr.app.MyClass$BodySubscribers (in unnamed module.....
On another note, the class you've mentioned seems to be packaged internal to the java.net.http module and should not be relied upon from your code. You must implement your own subscriber even if you desire a similar functionality as the code you're looking at. Since the module wouldn't be exporting it for a public use anyway.
You shouldn't use directly any class from any jdk.internal.* package. Have you tried using the public API HttpResponse.BodySubscribers.ofByteArray() instead?
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.
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).