When the file manager returns a file object corresponding to an entry of a multi-release jar, JavaFileObject#toUri returns a URI that cannot be used to read the jar entry.
E.g. in the following example, the returned URI is:
jar:file:///tmp/tmp.28UW6cUPnS/mr.jar!/module-info.class
But the entry in the jar is in the `META-INF/versions/9/` directory, and calling `uri.toURL().openConnection()` on the URI returned by the file object will fail. Reading the jar entry would require a URI like:
jar:file:///tmp/tmp.28UW6cUPnS/mr.jar!/META-INF/versions/9/module-info.class
Is this expected behaviour? Is there a way to get a URI from a file object that includes the MR-JAR subdirectory?
===
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.jar.JarEntry;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
public class Repro {
public static void main(String[] args) throws Exception {
StandardJavaFileManager fileManager =
ToolProvider.getSystemJavaCompiler()
.getStandardFileManager(null, Locale.ENGLISH, StandardCharsets.UTF_8);
Path path = Paths.get("mr.jar");
fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, List.of(path));
Iterator<String> options = Arrays.asList("--multi-release", "9").iterator();
fileManager.handleOption(options.next(), options);
Iterable<JavaFileObject> list =
fileManager.list(
StandardLocation.CLASS_PATH, "", EnumSet.allOf(JavaFileObject.Kind.class), false);
for (JavaFileObject f : list) {
System.err.println("JavaFileObject: " + f.getName());
System.err.println("JavaFileObject#toUri: " + f.toUri());
openUsingUri(f.toUri());
}
}
private static void openUsingUri(URI uri) throws IOException {
URLConnection connection = uri.toURL().openConnection();
if (connection instanceof JarURLConnection) {
try {
JarEntry entry = ((JarURLConnection) connection).getJarEntry();
System.err.println("JarEntry: " + entry.getName());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
===
$ echo 'module hello {}' > module-info.java
$ javac -d classes --release 9 module-info.java
$ jar --create --file mr.jar --release 9 -C classes .
$ java Repro
JavaFileObject: mr.jar(/module-info.class)
JavaFileObject#toUri: jar:file:///tmp/tmp.28UW6cUPnS/mr.jar!/module-info.class
java.io.FileNotFoundException: JAR entry module-info.class not found in /tmp/tmp.28UW6cUPnS/mr.jar
at java.base/sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:146)
at java.base/sun.net.www.protocol.jar.JarURLConnection.getJarEntry(JarURLConnection.java:97)
at Repro.openUsingUri(Repro.java:40)
at Repro.main(Repro.java:32)
E.g. in the following example, the returned URI is:
jar:file:///tmp/tmp.28UW6cUPnS/mr.jar!/module-info.class
But the entry in the jar is in the `META-INF/versions/9/` directory, and calling `uri.toURL().openConnection()` on the URI returned by the file object will fail. Reading the jar entry would require a URI like:
jar:file:///tmp/tmp.28UW6cUPnS/mr.jar!/META-INF/versions/9/module-info.class
Is this expected behaviour? Is there a way to get a URI from a file object that includes the MR-JAR subdirectory?
===
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.jar.JarEntry;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
public class Repro {
public static void main(String[] args) throws Exception {
StandardJavaFileManager fileManager =
ToolProvider.getSystemJavaCompiler()
.getStandardFileManager(null, Locale.ENGLISH, StandardCharsets.UTF_8);
Path path = Paths.get("mr.jar");
fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, List.of(path));
Iterator<String> options = Arrays.asList("--multi-release", "9").iterator();
fileManager.handleOption(options.next(), options);
Iterable<JavaFileObject> list =
fileManager.list(
StandardLocation.CLASS_PATH, "", EnumSet.allOf(JavaFileObject.Kind.class), false);
for (JavaFileObject f : list) {
System.err.println("JavaFileObject: " + f.getName());
System.err.println("JavaFileObject#toUri: " + f.toUri());
openUsingUri(f.toUri());
}
}
private static void openUsingUri(URI uri) throws IOException {
URLConnection connection = uri.toURL().openConnection();
if (connection instanceof JarURLConnection) {
try {
JarEntry entry = ((JarURLConnection) connection).getJarEntry();
System.err.println("JarEntry: " + entry.getName());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
===
$ echo 'module hello {}' > module-info.java
$ javac -d classes --release 9 module-info.java
$ jar --create --file mr.jar --release 9 -C classes .
$ java Repro
JavaFileObject: mr.jar(/module-info.class)
JavaFileObject#toUri: jar:file:///tmp/tmp.28UW6cUPnS/mr.jar!/module-info.class
java.io.FileNotFoundException: JAR entry module-info.class not found in /tmp/tmp.28UW6cUPnS/mr.jar
at java.base/sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:146)
at java.base/sun.net.www.protocol.jar.JarURLConnection.getJarEntry(JarURLConnection.java:97)
at Repro.openUsingUri(Repro.java:40)
at Repro.main(Repro.java:32)