Module resource loading
Alexey Gavrilov
Alexey1.Gavrilov at gmail.com
Tue Jun 8 19:20:29 UTC 2021
Hi,
While working on modularization of my application I've noticed
encapsulation rules for non-class-file resources are different depending on
the resource directory names.
It was not quite obvious for me and my colleagues even though it is a part
of the spec. I wonder if this can be possibly clarified in the
documentation. Please see an example below.
The specification for [getResourceAsStream][1] says that the resource is
encapsulated if a *package name* is derived from its name.
So if the [resource’s directory name is NOT a valid Java identifier][2], it
is NOT encapsulated. Which means that if a module has a resource located
under, for example, a directory named `dir-3` (containing a non-valid
character `-` in its name) it will always be accessible from outside of the
module.
Here is an example of two Java modules ([source code in GitHub][3]).
*Module 1* consists of the following resource files:
├── dir-3
│ └── resource3.txt
├── dir1
│ └── resource1.txt
├── dir2
│ └── resource2.txt
└── root.txt
and `module-info.java`:
module module_one {
opens dir1;
}
*Module 2* requires *Module 1* in `module-info.java`:
module module_two {
requires module_one;
}
and has a sample main class for loading various resource files:
package module2;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
loadResource("root.txt", "From module's root directory");
loadResource("dir1/resource1.txt", "From opened package `dir1`");
loadResource("dir2/resource2.txt", "From internal package `dir2`");
loadResource("dir-3/resource3.txt", "From directory `dir-3` with
non-Java name");
}
public static void loadResource(String name, String comment) throws
IOException {
// module2 application class loader
final var classLoader = Main.class.getClassLoader();
try (var in = classLoader.getResourceAsStream(name)) {
System.out.println();
System.out.println("// " + comment);
System.out.println(name + ": " + (in != null));
}
}
}
Running the code above gives the following output:
// From module's root directory
root.txt: true
// From opened package `dir1`
dir1/resource1.txt: true
// From internal package `dir2`
dir2/resource2.txt: false
// From directory `dir-3` with non-Java name
dir-3/resource3.txt: true
As you can see the resource file from the root directory and from the
`dir-3` directory are not encapsulated, therefore *Module 2* can load them.
The package `dir1` is encapsulated but unconditionally opened. *Module 2*
can load it as well.
The package `dir2` is encapsulated and not opened. *Module 2* cannot load
it.
Note that *Module 2* cannot contain its own resources under `dir1` and
`dir2` directories because they are already encapsulated in *Module 1*. If
you try adding `dir1` you will get the following error:
Error occurred during initialization of boot layer
java.lang.LayerInstantiationException: Package dir1 in both module
module_one and module module_two
Regards,
Alexey
[1]:
https://docs.oracle.com/javase/9/docs/api/java/lang/Module.html#getResourceAsStream-java.lang.String-
[2]:
https://docs.oracle.com/javase/8/docs/technotes/guides/lang/resources.html
[3]: https://github.com/agavrilov76/jpms-resource
More information about the jigsaw-dev
mailing list