In a recent article I discovered that resource loading between named modules is possible only when (1) the resource is in same module as the resource-anchor class, and (2) the caller of resourceAnchorClass.getResources()
is in same module as the resource. But what about the "unnamed module", i.e. legacy classes that are not inside a module? Can a module-encapsulated class read a resource inside the unnamed module, and can a legacy class read a resource inside a named module?
module-info.java
descriptor and package directories below. It explicitly exports packages and/or service classes, all other contents can not be seen from outside. Any package or service it needs from outside must be listed in a "requires" or "uses" statement in module-info.java
. It can not see the unnamed module, but can see packages in automatic modules when it "requires" them. Module dependency graphs do not allow cycles. requires
statement in module-info.java
that points to the package of the classes to be used. This example builds upon the sources of my recent Blog about resource loading with modules. I added another resource and resource-anchor class to fri.module2
, and I added the "unnamed module". Mind that using underscore ('_') in a package name is possible but unwanted, Java plans to make it a reserved character in future.
The fri.module_unnamed
project has CLASSPATH references to both fri.module1
and fri.module2
. The module fri.module2
has a MODULEPATH dependency on fri.module1
:
Here are the directories and files of the three involved Eclipse projects, including all source code (click to expand):
package fri.module1;
import java.io.InputStream;
import java.net.URL;
public final class ResourceUtil
{
public static String asText(Class<?> resourceAnchor, String fileName) throws Exception {
assert fileName != null;
return asText(resourceAnchor.getResourceAsStream(fileName));
}
public static String asText(URL url) throws Exception {
assert url != null;
return asText(url.openStream());
}
public static String asText(InputStream inputStream) throws Exception {
assert inputStream != null;
final StringBuilder sb = new StringBuilder();
int c;
try (inputStream) { // will close stream at end
while ((c = inputStream.read()) != -1)
sb.append((char) c);
}
return sb.toString();
}
private ResourceUtil() {} // do not instantiate
}
package fri.module1;
public class ResourceAnchor
{
}
I am resource of module-one.
module fri.module1
{
exports fri.module1;
}
package fri.module2;
public class ResourceAnchorInModule2
{
}
I am resource of module 2.
module fri.module2
{
requires fri.module1;
}
package fri.module_unnamed;
import java.net.URL;
import fri.module1.ResourceAnchor;
import fri.module1.ResourceUtil;
import fri.module2.ResourceAnchorInModule2;
public class Main
{
public static void main(String[] args) throws Exception {
// direct access of a resource in a named module: works!
final URL url = ResourceAnchor.class.getResource("resource.txt");
System.out.println(url);
// access of a resource in unnamed module through a named module: works!
final String text = ResourceUtil.asText(ResourceAnchorInUnnamedModule.class, "resource_in_unnamed_module.txt");
System.out.println(text);
// access of a resource in a named module through a named module: works!
final String text2 = ResourceUtil.asText(ResourceAnchorInModule2.class, "resource_in_module2.txt");
System.out.println(text2);
}
private Main() {} // do not instantiate
}
package fri.module_unnamed;
public class ResourceAnchorInUnnamedModule
{
}
I am resource of the unnamed module.
Mind that module_unnamed
has no module_info.java
file because the JPMS "unnamed module" is not a module.
We are going to execute the test Main
in the unnamed module fri.module_unnamed
:
1 | package fri.module_unnamed; |
Output is:
file:/media/disk2/java/workspace-ee2/fri.module1/target/fri/module1/resource.txt
I am resource of the unnamed module.
I am resource of module 2.
All three resource loading statements succeeded.
But what do I test here?
In line 12, I directly load a resource from fri.module1
. That would not be possible for a named module. When getResource()
delivers non-null, you always should be able to read the returned URL's content, so this was successful.
In line 16, I ask the ResourceUtil
class in fri.module1
to load a resource using a resource-anchor that is in the unnamed module. Although classes in named modules normally can not use classes of the unnamed module, this works.
In line 20, I use ResourceUtil
in fri.module1
to load a resource in fri.module2
, with a resource-anchor also being in fri.module2
. That means, the getResource()
call happens in fri.module1
, but both the resource-anchor and the resource are in fri.module2
. Also not possible between named modules, but works when executed by the unnamed module.
Resource loading is free like it always was for non-modularized classes, most likely due to backward compatibilty reasons. We can let read resources in the unnamed module through module classes, and we even can read resources inside named modules through classes of other named modules
ɔ⃝ Fritz Ritzberger, 2020-05-24