1/* 2 * Copyright (c) 2016, 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/* 25 * @test 26 * @bug 8151486 27 * @summary Call Class.forName() on the system classloader from a class loaded 28 * from a custom classloader. 29 * @library /lib/testlibrary 30 * @build jdk.testlibrary.Utils JarUtils 31 * @build ClassForName ClassForNameLeak 32 * @run main/othervm/policy=test.policy -Djava.security.manager ClassForNameLeak 33 */ 34 35import java.lang.ref.PhantomReference; 36import java.lang.ref.Reference; 37import java.lang.ref.ReferenceQueue; 38import java.net.URL; 39import java.net.URLClassLoader; 40import java.nio.file.FileSystems; 41import java.nio.file.Files; 42import java.nio.file.Path; 43import java.nio.file.Paths; 44import java.util.List; 45import java.util.concurrent.Callable; 46import java.util.concurrent.ExecutorService; 47import java.util.concurrent.Executors; 48import java.util.concurrent.Future; 49import java.util.stream.Collectors; 50import java.util.stream.Stream; 51import jdk.testlibrary.Utils; 52 53/* 54 * Create .jar, load ClassForName from .jar using a URLClassLoader 55 */ 56public class ClassForNameLeak { 57 private static final long TIMEOUT = (long)(5000.0 * Utils.TIMEOUT_FACTOR); 58 private static final String TESTCLASSES = System.getProperty("test.classes", "."); 59 private static final String CLASSFILENAME = "ClassForName.class"; 60 private static final int THREADS = 10; 61 private static final ReferenceQueue<ClassLoader> rq = new ReferenceQueue<>(); 62 63 // Use a new classloader to load the ClassForName class, then run its 64 // Runnable. 65 public static PhantomReference<ClassLoader> loadAndRun(Path jarFilePath) 66 throws Exception { 67 ClassLoader classLoader = new URLClassLoader( 68 new URL[]{jarFilePath.toUri().toURL()}) { 69 @Override public String toString() { return "LeakedClassLoader"; } 70 }; 71 72 Class<?> loadClass = Class.forName("ClassForName", true, classLoader); 73 ((Runnable) loadClass.newInstance()).run(); 74 75 PhantomReference<ClassLoader> ref = new PhantomReference<>(classLoader, rq); 76 System.out.println("returning phantom ref: " + ref + " to " + classLoader); 77 return ref; 78 } 79 80 public static void main(final String[] args) throws Exception { 81 // Create a temporary .jar file containing ClassForName.class 82 Path testClassesDir = Paths.get(TESTCLASSES); 83 Path jarFilePath = Files.createTempFile("cfn", ".jar"); 84 JarUtils.createJarFile(jarFilePath, testClassesDir, CLASSFILENAME); 85 jarFilePath.toFile().deleteOnExit(); 86 87 // Remove the ClassForName.class file that jtreg built, to make sure 88 // we're loading from the tmp .jar 89 Path classFile = FileSystems.getDefault().getPath(TESTCLASSES, 90 CLASSFILENAME); 91 Files.delete(classFile); 92 93 // Make simultaneous calls to the test method, to stress things a bit 94 ExecutorService es = Executors.newFixedThreadPool(THREADS); 95 96 List<Callable<PhantomReference<ClassLoader>>> callables = 97 Stream.generate(() -> { 98 Callable<PhantomReference<ClassLoader>> cprcl = () -> { 99 return loadAndRun(jarFilePath); 100 }; 101 return cprcl; 102 }).limit(THREADS).collect(Collectors.toList()); 103 104 List<Future<PhantomReference<ClassLoader>>> refs = es.invokeAll(callables); 105 106 // Give the GC a chance to enqueue the PhantomReferences 107 for (int i = 0; i < 10; i++) { 108 System.gc(); 109 } 110 // Make sure all PhantomReferences to the leaked classloader are enqueued 111 for (int j = 0; j < THREADS; j++) { 112 Reference rmRef = rq.remove(TIMEOUT); 113 if (rmRef == null) { 114 throw new RuntimeException("ClassLoader was never enqueued!"); 115 } else { 116 System.out.println("Enqueued " + rmRef); 117 } 118 } 119 System.out.println("All Classloaders successfully enqued"); 120 } 121} 122