1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.File;
  25 import java.net.URL;
  26 import java.net.URLClassLoader;
  27 import java.nio.file.Path;
  28 import java.nio.file.Paths;
  29 import java.util.jar.Attributes;
  30 import java.util.jar.Manifest;
  31 import jdk.test.lib.util.JarUtils;
  32 import jdk.test.lib.compiler.InMemoryJavaCompiler;
  33 
  34 /*
  35  * @test
  36  * @bug 8216401 8235361
  37  * @summary Test classloading via JAR Class-Path entries
  38  * @library /test/lib
  39  *
  40  * @run main/othervm JarClassPathFileEntry
  41  * @run main/othervm -Djdk.net.URLClassPath.disableClassPathURLCheck=true JarClassPathFileEntry
  42  * @run main/othervm -Djdk.net.URLClassPath.disableClassPathURLCheck=false JarClassPathFileEntry
  43  */
  44 
  45 public class JarClassPathFileEntry {
  46     private final static boolean IS_WINDOWS = System.getProperty("os.name").startsWith("Windows");
  47 
  48     private final static String TEST_CLASSES = System.getProperty("test.classes");
  49     private final static String OTHER_DIR = TEST_CLASSES + "/OTHER/";
  50 
  51     private final static Path OTHER_JAR_PATH = Paths.get(OTHER_DIR, "Other.jar");
  52     private final static Path CONTEXT_JAR_PATH = Paths.get(TEST_CLASSES, "Context.jar");
  53 
  54     public static void main(String[] args) throws Throwable {
  55         String fileScheme = "file:" + (IS_WINDOWS ? toUnixPath(OTHER_JAR_PATH.toString())
  56                                                       :        OTHER_JAR_PATH.toString());        
  57         doTest(fileScheme);
  58         
  59         if (IS_WINDOWS) {
  60             // Relative URL encoding of absolute path, e.g. /C:\\path\\to\\file.jar
  61             String driveLetter = "/" + OTHER_JAR_PATH;
  62             doTest(driveLetter);
  63         }
  64     }
  65     
  66     /* Load a class from Other.jar via the given Class-Path entry */
  67     private static void doTest(String classPathEntry) throws Throwable {
  68         // Create Other.class in OTHER_DIR, off the default classpath
  69         byte klassbuf[] = InMemoryJavaCompiler.compile("Other",
  70                                                        "public class Other {}");
  71         ClassFileInstaller.writeClassToDisk("Other", klassbuf, OTHER_DIR);
  72 
  73         // Create Other.jar in OTHER_DIR
  74         JarUtils.createJarFile(OTHER_JAR_PATH,
  75                                Paths.get(OTHER_DIR),
  76                                Paths.get(OTHER_DIR, "Other.class"));
  77 
  78         // Create Context.class
  79         klassbuf = InMemoryJavaCompiler.compile("Context",
  80                                                 "public class Context {}");
  81         ClassFileInstaller.writeClassToDisk("Context", klassbuf, TEST_CLASSES);
  82 
  83         // Create Context.jar w/ "file:" entry for Other.jar
  84         Manifest mf = new Manifest();
  85         Attributes attrs = mf.getMainAttributes();
  86         attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
  87 
  88         attrs.put(Attributes.Name.CLASS_PATH, classPathEntry);
  89 
  90         System.out.println("Creating Context.jar with Class-Path: " + classPathEntry);
  91         JarUtils.createJarFile(CONTEXT_JAR_PATH, mf,
  92                                Paths.get(TEST_CLASSES),
  93                                Paths.get(TEST_CLASSES, "Context.class"));
  94 
  95         // Use URLClassLoader w/ Context.jar to load Other.class, which will
  96         // load via the Class-Path entry
  97         URL url = CONTEXT_JAR_PATH.toUri().toURL();
  98         URLClassLoader ucl = new URLClassLoader("TestURLClassLoader",
  99                                                 new URL[]{ url },
 100                                                 null); // don't delegate to App CL
 101         Class<?> otherClass = Class.forName("Other", true, ucl); // ClassNotFoundException -> fail
 102         System.out.println("Loaded: " + otherClass);
 103     }
 104 
 105     /* Convert a Windows path to a unix-style path, and remove any drive letter */
 106     private static String toUnixPath(String orig) {
 107         String retVal = new File(orig).toURI().getPath();
 108         int colonAt = retVal.indexOf(':');
 109 
 110         if (colonAt != -1 && colonAt < 3) {
 111             retVal = retVal.substring(colonAt + 1); // Start after the drive letter
 112         }
 113         return retVal;
 114     }
 115 }