From Java 11, how can I read the content of another runtime image?
In order to list the content of a Java runtime image, JEP 220 suggests the following solution:
A built-in NIO
FileSystemprovider for thejrtURL scheme ensures that development tools can enumerate and read the class and resource files in a run-time image by loading theFileSystemnamed by the URLjrt:/, as follows:FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); byte[] jlo = Files.readAllBytes(fs.getPath("modules", "java.base", "java/lang/Object.class"));
This snippet works and will allow me to read the content of java/lang/Object.class in the runtime image of the Java installation that is executing the code.
How can I get it to read the content of java/lang/Object.class in another Java installation, given its java home?
I have read this SO answer which explains how to read a Java runtime image's content from a Java 8 runtime. Unfortunately, this won't work with newer Java runtimes, since, I believe, the filesystem for jrt:/ will always point to the current runtime image.
You may still use jrt:/ scheme as described in this answer, you just need to provide an alternative java.home path in the environment argument when creating a FileSystem object:
public static void listModules(String javaHome) throws IOException {
FileSystem fs = FileSystems.newFileSystem(
URI.create("jrt:/"),
Collections.singletonMap("java.home", javaHome));
try (Stream<Path> stream = Files.list(fs.getPath("/modules"))) {
stream.forEach(System.out::println);
}
}
Or, to read a single resource:
public static byte[] readResource(String javaHome, String module, String path) throws IOException {
FileSystem fs = FileSystems.newFileSystem(
URI.create("jrt:/"),
Collections.singletonMap("java.home", javaHome));
return Files.readAllBytes(fs.getPath("modules", module, path));
}
I think what you want is impossible. To wit:
Paths.get(pathToJdk8Home, "jre", "lib", "rt.jar") to exist, which you can then turn into a URL (you're looking for jar:file:/that/path), and you can then toss that URL at FileSystems.newFileSystem), see this documentation for more.The jrt scheme can only load data from jmods that are actually 'loaded' into the VM's mod base, which I gather is explicitly not what you want (in fact, I'm pretty sure you cannot load e.g. the JDK11 core jmods into a JDK14, as it already loaded its jmods, and you'd get a split package violation). The jrt:// URL scheme, per its spec, isn't base file system related. You specify a module name (or nothing, and you get all loaded modules as one file system). There is no place for you to list a JDK installation path or jmod file, so that can't help you either.
Thus, you have only two options:
The hackery would involve:
Files.walk) to find a file named rt.jar. If it's there, load it up as ZipFileSystem and carry on. Modules 'do not exist', just turn any desired class into a path by replacing dots with slashes and appending .class (note that you'll need the binary name; e.g. package com.foo; class Outer { class Inner {}} means you want the name of Inner to be com.foo.Outer$Inner, so that you turn that into /com/foo/Outer$Inner.class).JDK_HOME/jmods/java.base.jmod, and throw that at ZipFileSystem. A given class is in subdir classes. So, you're looking for e.g. the entry classes/java/lang/Object.class within the zip (that jmod is the zip). However, festoon this code with comments stating that this is a total hack and there is zero guarantee that this will work in the future. I can tell you, however, that JDK16, at least, still has zip-based jmod files.ProcessBuilder to exec List.of("JDK_HOME/bin/jmod" /* or jmod.exe, you'll have to check which one to call! */, "extract", "JDK_HOME/jmods/java.base.jmod"), but note that this will extract all of those files into the current working directory (you can set the cwd for the invoked process to be some dir you just created for the purpose of being filled with the files inside). Quite a big bazooka if all you wanted was the one file. (You can also use the --dir option instead). The advantage is that this will still work even if hypothetically JDK17 is using some different format; presumably JDK17 will still have both bin/jmod as well as jmods/java.base.jmod, and the bin/jmod of JDK17 should be able to unpack the jmod files in your JDK17 installation. Even if you are running all this from e.g. JDK16 which wouldn't be able to read them.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With