[PATCH] 8218268: Javac treats Manifest Class-Path entries as Paths instead of URLs

Donald Kwakkel dkwakkel at gmail.com
Thu Feb 28 12:13:57 UTC 2019


Hi All,

This is my first contribution to openjdk, so hope to learn a lot!

I created a fix + tests for https://bugs.openjdk.java.net/browse/JDK-8218268.
Now the manifest classpath is behaving the same in javac and java for
file paths.

This fixes:
1. The Java Compiler treats the entries as URLs, according to spec:
https://docs.oracle.com/en/java/javase/11/docs/specs/jar/jar.html#class-path-attribute
2. The behavior of the JVM ClassLoader and the Java Compiler is equal
(for file urls)
3. The behavior is backwards compatible (it was broken in  javac 9-13)

The current patch contains a System.err because I do not know how to
(debug) log information in the jdk. Hope someone can explain how to do
this the right way.

See below the patch. Looking forward for a sponsor to adopt/work on my change!

Kind Regards, Donald Kwakkel

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java
b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java
@@ -26,7 +26,10 @@
 package com.sun.tools.javac.file;

 import java.io.IOException;
-import java.nio.file.FileSystems;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.spi.FileSystemProvider;
@@ -90,7 +93,6 @@
     }

     public List<Path> getJarClassPath(Path file) throws IOException {
-        Path parent = file.getParent();
         try (JarFile jarFile = new JarFile(file.toFile())) {
             Manifest man = jarFile.getManifest();
             if (man == null)
@@ -100,22 +102,27 @@
             if (attr == null)
                 return Collections.emptyList();

-            String path = attr.getValue(Attributes.Name.CLASS_PATH);
-            if (path == null)
+            String classpath = attr.getValue(Attributes.Name.CLASS_PATH);
+            if (classpath == null)
                 return Collections.emptyList();

-            List<Path> list = new ArrayList<>();
+            // TODO: Must use the same code as
jdk.internal.loader.URLClassPath.JarLoader.parseClassPath(base, path)
+            StringTokenizer st = new StringTokenizer(classpath);
+            List<Path> paths = new ArrayList<>(st.countTokens());
+            while (st.hasMoreTokens()) {
+                String path = st.nextToken();
+                try {
+                    // Use getCanonicalFile just as
jdk.internal.loader.URLClassPath.toFileURL
+                    URL base =
file.toFile().getCanonicalFile().toURI().toURL();
+                    URI uri = new URL(base, path).toURI();

-            for (StringTokenizer st = new StringTokenizer(path);
-                 st.hasMoreTokens(); ) {
-                String elt = st.nextToken();
-                Path f = FileSystems.getDefault().getPath(elt);
-                if (!f.isAbsolute() && parent != null)
-                    f = parent.resolve(f).toAbsolutePath();
-                list.add(f);
+                    // Should return uri, see comment on
com.sun.tools.javac.file.Locations.SearchPath.addJarClassPath
+                    paths.add(Path.of(uri));
+                } catch (MalformedURLException | URISyntaxException e) {
+                    System.err.println("Class-Path entry: \"" + path
+ "\" ignored in JAR file \"" + file + "\": " + e.getMessage());
+                }
             }
-
-            return list;
+            return paths;
         }
     }

diff --git a/test/langtools/tools/javac/Paths/UrlClassPath.sh
b/test/langtools/tools/javac/Paths/UrlClassPath.sh
new file mode 100755
--- /dev/null
+++ b/test/langtools/tools/javac/Paths/UrlClassPath.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+# @test
+# @bug 8218268
+# @summary Test handling of urls (with spaces) in the Class-Path
attribute in jar file manifests
+# @author Donald Kwakkel
+#
+# @run shell UrlClassPath.sh
+
+# To run this test manually, simply do ./UrlClassPath.sh
+
+. ${TESTSRC-.}/Util.sh
+
+set -u
+
+Cleanup() {
+    Sys rm -rf "x y" z Hello.java Hello.class Main.java Main.class
MANIFEST.MF hellocp.jar
+}
+
+Cleanup
+Sys mkdir "x y"
+
+#----------------------------------------------------------------
+# Create mutually referential jar files
+#----------------------------------------------------------------
+cat >Hello.java <<EOF
+public class Hello {
+  public static String hello() {
+    return "hello";
+  }
+}
+EOF
+
+cat >Main.java <<EOF
+public class Main {
+  public static void main(String[] args) {
+    System.out.println(Hello.hello());
+  }
+}
+EOF
+
+Sys "$javac" Hello.java
+Sys "$jar" cf "x y/hello.jar" Hello.class
+Sys rm -rf Hello.class Hello.java
+
+createManifestJar() {
+  MkManifestWithClassPath "${1}"
+  Sys "$jar" cmf MANIFEST.MF "${2}"
+}
+
+runJava() {
+  Success "$javac" ${TESTTOOLVMOPTS} -cp ".:${1}" Main.java
+  # Bug: jdk.net.URLClassPath.disableClassPathURLCheck meaning is
inverted and should be default enabled according to spec
+  Success "$java"  ${TESTVMOPTS}
-Djdk.net.URLClassPath.disableClassPathURLCheck=false -cp ".:${1}"
Main
+}
+
+doTest() {
+  createManifestJar "${1}" hellocp.jar
+  runJava "${2}"
+}
+
+# Test 1: Compiling and run with directly adding classpath with space
+doTest "x%20y/hello.jar" "x y/hello.jar"
+
+# Test 2: Compiling and run with adding classpath with space via manifest
+doTest "x%20y/hello.jar" "hellocp.jar"
+
+# Test 3: classpath with file url path
+doTest "file:"`pwd`"/x%20y/hello.jar" "hellocp.jar"
+
+# Test 4: classpath with file uri path
+doTest "file://"`pwd`"/x%20y/hello.jar" "hellocp.jar"
+
+# Test 5: garbage before, right jar must still be used
+doTest "notexisting://garbage x%20y/hello.jar" "hellocp.jar"
+
+# Test 6: linked jar
+Sys mkdir "z"
+createManifestJar "x%20y/hello.jar" hellocp.jar
+Sys ln -s ../hellocp.jar z/hellocp.jar
+runJava "z/hellocp.jar"
+
+# Test 7: only garbage, should fail
+createManifestJar "notexisting://garbage" hellocp.jar
+Failure "$javac" ${TESTTOOLVMOPTS} -cp ".:hellocp.jar" Main.java
+Failure "$java" ${TESTVMOPTS}      -cp ".:hellocp.jar" Main.java
+
+Cleanup
+
+Bottom Line


More information about the jdk-dev mailing list