Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8271079

JavaFileObject#toUri and multi-release jars

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P3 P3
    • 19
    • None
    • tools
    • None
    • b02

      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)

            cstein Christian Stein
            cushon Liam Miller-Cushon
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: