8169909: java agent fails to add to class path when the initial module is a named module
authormchung
Sun, 20 Nov 2016 07:57:57 -0800
changeset 42170 0bb91d845f04
parent 42169 b5f96c169364
child 42171 82cbe399c3fe
8169909: java agent fails to add to class path when the initial module is a named module Reviewed-by: alanb
jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java
jdk/test/java/lang/instrument/modules/AppendToClassPathModuleTest.java
jdk/test/java/lang/instrument/modules/AppendToClassPathModuleTest.sh
jdk/test/java/lang/instrument/modules/src/test/jdk/test/Main.java
jdk/test/java/lang/instrument/modules/src/test/module-info.java
jdk/test/tools/launcher/modules/classpath/JavaClassPathTest.java
jdk/test/tools/launcher/modules/classpath/src/m/jdk/test/Main.java
--- a/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Sat Nov 19 12:22:35 2016 +0100
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Sun Nov 20 07:57:57 2016 -0800
@@ -72,13 +72,13 @@
         // If neither is specified then default to -cp <working directory>
         // If -cp is not specified and -m is specified, the value of
         // java.class.path is an empty string, then no class path.
-        URLClassPath ucp = null;
+        URLClassPath ucp = new URLClassPath(new URL[0]);
         String mainMid = System.getProperty("jdk.module.main");
         String cp = System.getProperty("java.class.path");
         if (cp == null)
             cp = "";
         if (mainMid == null || cp.length() > 0)
-            ucp = toURLClassPath(cp);
+            addClassPathToUCP(cp, ucp);
 
         // create the class loaders
         BOOT_LOADER = new BootClassLoader(bcp);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/modules/AppendToClassPathModuleTest.java	Sun Nov 20 07:57:57 2016 -0800
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016, 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 8169909
+ * @library src /lib/testlibrary
+ * @build test/*
+ * @run shell AppendToClassPathModuleTest.sh
+ * @run main AppendToClassPathModuleTest
+ */
+
+import java.util.Map;
+import static jdk.testlibrary.ProcessTools.*;
+
+/**
+ * Launch a modular test with no class path and no CLASSPATH set.
+ * The java agent appends to the "hidden" directory to the class path
+ * at runtime.
+ */
+public class AppendToClassPathModuleTest {
+    public static void main(String... args) throws Throwable {
+        String modulepath = System.getProperty("test.module.path");
+        ProcessBuilder pb =
+            createJavaProcessBuilder("-javaagent:Agent.jar",
+                                     "--module-path", modulepath,
+                                     "-m", "test/jdk.test.Main");
+
+        // remove CLASSPATH environment variable
+        Map<String,String> env = pb.environment();
+        String value = env.remove("CLASSPATH");
+
+        int exitCode = executeCommand(pb).getExitValue();
+        if (exitCode != 0) {
+            throw new RuntimeException("Test failed: " + exitCode);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/modules/AppendToClassPathModuleTest.sh	Sun Nov 20 07:57:57 2016 -0800
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2016, 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.
+#
+
+if [ "${TESTSRC}" = "" ]
+then
+  echo "TESTSRC not set.  Test cannot execute.  Failed."
+  exit 1
+fi
+echo "TESTSRC=${TESTSRC}"
+
+if [ "${TESTJAVA}" = "" ]
+then
+  echo "TESTJAVA not set.  Test cannot execute.  Failed."
+  exit 1
+fi
+echo "TESTJAVA=${TESTJAVA}"
+
+if [ "${COMPILEJAVA}" = "" ]
+then
+  COMPILEJAVA="${TESTJAVA}"
+fi
+echo "COMPILEJAVA=${COMPILEJAVA}"
+
+if [ "${TESTCLASSES}" = "" ]
+then
+  echo "TESTCLASSES not set.  Test cannot execute.  Failed."
+  exit 1
+fi
+
+echo "TESTCLASSES=${TESTCLASSES}"
+
+mkdir -p hidden
+${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
+    -d hidden ${TESTSRC}/../ExampleForClassPath.java
+
+mkdir -p classes
+${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
+    -d classes ${TESTSRC}/../InstrumentationHandoff.java
+
+echo "Manifest-Version: 1.0" > Agent.mf
+echo "Class-Path: hidden/" >> Agent.mf
+echo "Premain-Class: InstrumentationHandoff" >> Agent.mf
+
+${TESTJAVA}/bin/jar ${TESTTOOLVMOPTS} cvfm Agent.jar \
+    Agent.mf -C classes InstrumentationHandoff.class
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/modules/src/test/jdk/test/Main.java	Sun Nov 20 07:57:57 2016 -0800
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+package jdk.test;
+
+/**
+ * Launched by AppendToClassPathModuleTest.
+ */
+public class Main {
+    public static void main(String... args) throws Exception {
+        // "java.class.path" system property is expected to be empty.
+        String value = System.getProperty("java.class.path");
+        if (!value.isEmpty()) {
+            throw new RuntimeException("Non-empty java.class.path=" + value);
+        }
+
+        // load the "hidden" class that should be loaded by the system loader
+        Class<?> c = Class.forName("ExampleForClassPath");
+        if (c.getClassLoader() != ClassLoader.getSystemClassLoader()) {
+            throw new RuntimeException(c + " loaderd by " + c.getClassLoader());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/modules/src/test/module-info.java	Sun Nov 20 07:57:57 2016 -0800
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+module test {
+}
--- a/jdk/test/tools/launcher/modules/classpath/JavaClassPathTest.java	Sat Nov 19 12:22:35 2016 +0100
+++ b/jdk/test/tools/launcher/modules/classpath/JavaClassPathTest.java	Sun Nov 20 07:57:57 2016 -0800
@@ -21,12 +21,14 @@
  * questions.
  */
 
+import java.io.BufferedWriter;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.spi.ToolProvider;
 
 import jdk.testlibrary.OutputAnalyzer;
 import org.testng.annotations.BeforeTest;
@@ -42,6 +44,7 @@
  * @summary Test the default class path if -Djava.class.path is set
  * @library /lib/testlibrary
  * @modules jdk.compiler
+ *          jdk.jartool
  * @build CompilerUtils jdk.testlibrary.*
  * @run testng JavaClassPathTest
  */
@@ -50,6 +53,7 @@
     private static final Path SRC_DIR = Paths.get(System.getProperty("test.src"),
                                                   "src");
     private static final Path MODS_DIR = Paths.get("mods");
+    private static final Path LIB_DIR = Paths.get("lib");
     private static final String TEST_MODULE = "m";
     private static final String TEST_MAIN = "jdk.test.Main";
 
@@ -66,27 +70,47 @@
 
         Path res = Paths.get("jdk/test/res.properties");
         Files.createFile(res);
+
+        ToolProvider jartool = ToolProvider.findFirst("jar").orElseThrow(
+            () -> new RuntimeException("jar tool not found")
+        );
+
+        Path jarfile = LIB_DIR.resolve("m.jar");
+        Files.createDirectories(LIB_DIR);
+        assertTrue(jartool.run(System.out, System.err, "cfe",
+                               jarfile.toString(), TEST_MAIN,
+                               file.toString()) == 0);
+
+        Path manifest = LIB_DIR.resolve("manifest");
+        try (BufferedWriter writer = Files.newBufferedWriter(manifest)) {
+            writer.write("CLASS-PATH: lib/m.jar");
+        }
+        jarfile = LIB_DIR.resolve("m1.jar");
+        assertTrue(jartool.run(System.out, System.err, "cfme",
+                               jarfile.toString(), manifest.toString(), TEST_MAIN,
+                               file.toString()) == 0);
     }
 
     @DataProvider(name = "classpath")
     public Object[][] classpath() {
         return new Object[][]{
             // true indicates that class path default to current working directory
-            { "",                              true  },
-            { "-Djava.class.path",             true  },
-            { "-Djava.class.path=",            true  },
-            { "-Djava.class.path=.",           true  },
+            { "",                              "."  },
+            { "-Djava.class.path",             "."  },
+            { "-Djava.class.path=",            ""  },
+            { "-Djava.class.path=.",           "."  },
         };
     }
 
     @Test(dataProvider = "classpath")
-    public void testUnnamedModule(String option, boolean expected) throws Throwable {
+    public void testUnnamedModule(String option, String expected) throws Throwable {
         List<String> args = new ArrayList<>();
         if (!option.isEmpty()) {
             args.add(option);
         }
         args.add(TEST_MAIN);
-        args.add(Boolean.toString(expected));
+        args.add(Boolean.toString(true));
+        args.add(expected);
 
         assertTrue(execute(args).getExitValue() == 0);
     }
@@ -95,15 +119,14 @@
     public Object[][] moduleAndClassPath() {
         return new Object[][]{
             // true indicates that class path default to current working directory
-            { "",                              false  },
-            { "-Djava.class.path",             false  },
-            { "-Djava.class.path=",            false  },
-            { "-Djava.class.path=.",           true   },
+            { "",                              ""  },
+            { "-Djava.class.path",             ""  },
+            { "-Djava.class.path=",            ""  },
         };
     }
 
     @Test(dataProvider = "moduleAndClassPath")
-    public void testNamedModule(String option, boolean expected) throws Throwable {
+    public void testNamedModule(String option, String expected) throws Throwable {
         List<String> args = new ArrayList<>();
         if (!option.isEmpty()) {
             args.add(option);
@@ -112,7 +135,61 @@
         args.add(MODS_DIR.toString());
         args.add("-m");
         args.add(TEST_MODULE + "/" + TEST_MAIN);
-        args.add(Boolean.toString(expected));
+        // not default to CWD
+        args.add(Boolean.toString(false));
+        args.add(expected);
+
+
+        assertTrue(execute(args).getExitValue() == 0);
+    }
+
+    @Test
+    public void testClassPath() throws Throwable {
+        List<String> args = new ArrayList<>();
+        args.add("-Djava.class.path=.");
+        args.add("--module-path");
+        args.add(MODS_DIR.toString());
+        args.add("-m");
+        args.add(TEST_MODULE + "/" + TEST_MAIN);
+        args.add(Boolean.toString(true));
+        args.add(".");
+
+        assertTrue(execute(args).getExitValue() == 0);
+    }
+
+    @Test
+    public void testJAR() throws Throwable {
+        String jarfile = LIB_DIR.resolve("m.jar").toString();
+        List<String> args = new ArrayList<>();
+        args.add("-jar");
+        args.add(jarfile);
+        args.add(Boolean.toString(false));
+        args.add(jarfile);
+
+        assertTrue(execute(args).getExitValue() == 0);
+    }
+
+    /*
+     * Test CLASS-PATH attribute in manifest
+     */
+    @Test
+    public void testClassPathAttribute() throws Throwable {
+        String jarfile = LIB_DIR.resolve("m1.jar").toString();
+
+        List<String> args = new ArrayList<>();
+        args.add("-jar");
+        args.add(jarfile);
+        args.add(Boolean.toString(false));
+        args.add(jarfile);
+
+        assertTrue(execute(args).getExitValue() == 0);
+
+        args.clear();
+        args.add("-cp");
+        args.add(jarfile);
+        args.add(TEST_MAIN);
+        args.add(Boolean.toString(false));
+        args.add(jarfile);
 
         assertTrue(execute(args).getExitValue() == 0);
     }
--- a/jdk/test/tools/launcher/modules/classpath/src/m/jdk/test/Main.java	Sat Nov 19 12:22:35 2016 +0100
+++ b/jdk/test/tools/launcher/modules/classpath/src/m/jdk/test/Main.java	Sun Nov 20 07:57:57 2016 -0800
@@ -29,13 +29,14 @@
     static final String JAVA_CLASS_PATH = "java.class.path";
 
     public static void main(String[] args) throws Exception {
+        boolean expected = args[0].equals("true");
+        String cpath = args.length > 1 ? args[1] : "";
         String value = System.getProperty(JAVA_CLASS_PATH);
-        if (value == null) {
-            throw new RuntimeException(JAVA_CLASS_PATH + " is expected non-null" +
-                " for compatibility");
+        if (!value.equals(cpath)) {
+            throw new RuntimeException(JAVA_CLASS_PATH + "=" + value +
+                " expected=" + cpath);
         }
 
-        boolean expected = args[0].equals("true");
         ClassLoader loader = ClassLoader.getSystemClassLoader();
         URL url = loader.getResource("jdk/test/res.properties");
         if ((expected && url == null) || (!expected && url != null)) {