1/* 2 * Copyright (c) 2010, 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 * @test 25 * @bug 6936389 26 * 27 * @summary Test verifes that LogManager shutdown hook does not cause 28 * an application classloader leaks. 29 * 30 * @run main/othervm ClassLoaderLeakTest FontManagerTest 31 */ 32 33import java.awt.Font; 34import java.awt.Graphics; 35import java.io.File; 36import java.lang.ref.WeakReference; 37import java.lang.reflect.Constructor; 38import java.lang.reflect.Method; 39import java.net.MalformedURLException; 40import java.net.URL; 41import java.net.URLClassLoader; 42import java.util.concurrent.CountDownLatch; 43 44public class ClassLoaderLeakTest { 45 46 private static CountDownLatch doneSignal; 47 private static CountDownLatch launchSignal; 48 private static Throwable launchFailure = null; 49 50 public static void main(String[] args) { 51 doneSignal = new CountDownLatch(1); 52 launchSignal = new CountDownLatch(1); 53 54 String testcase = "FontManagerTest"; 55 56 if (args.length > 0) { 57 testcase = args[0]; 58 } 59 60 /* prepare test class loader */ 61 URL pwd = null; 62 try { 63 64 pwd = new File(System.getProperty("test.classes", ".")).toURL(); 65 } catch (MalformedURLException e) { 66 throw new RuntimeException("Test failed.", e); 67 } 68 System.out.println("PWD: " + pwd); 69 URL[] urls = new URL[]{pwd}; 70 71 MyClassLoader appClassLoader = new MyClassLoader(urls, "test0"); 72 WeakReference<MyClassLoader> ref = 73 new WeakReference<MyClassLoader>(appClassLoader); 74 75 ThreadGroup appsThreadGroup = new ThreadGroup("MyAppsThreadGroup"); 76 77 Runnable launcher = new TestLauncher(testcase); 78 79 Thread appThread = new Thread(appsThreadGroup, launcher, "AppThread-0"); 80 appThread.setContextClassLoader(appClassLoader); 81 82 appThread.start(); 83 appsThreadGroup = null; 84 appClassLoader = null; 85 launcher = null; 86 appThread = null; 87 88 /* wait for laucnh completion */ 89 try { 90 launchSignal.await(); 91 } catch (InterruptedException e) { 92 } 93 94 /* check if launch failed */ 95 if (launchFailure != null) { 96 throw new RuntimeException("Test failed.", launchFailure); 97 } 98 99 /* wait for test app excution completion */ 100 try { 101 doneSignal.await(); 102 } catch (InterruptedException e) { 103 } 104 105 /* give a chance to GC */ 106 waitAndGC(9); 107 108 if (ref.get() != null) { 109 throw new RuntimeException("Test failed: classloader is still alive"); 110 } 111 112 113 System.out.println("Test passed."); 114 } 115 116 private static class TestLauncher implements Runnable { 117 118 private String className; 119 120 public TestLauncher(String name) { 121 className = name; 122 } 123 124 public void run() { 125 try { 126 ClassLoader cl = 127 Thread.currentThread().getContextClassLoader(); 128 Class appMain = cl.loadClass(className); 129 Method launch = 130 appMain.getMethod("launch", doneSignal.getClass()); 131 132 Constructor c = appMain.getConstructor(); 133 134 Object o = c.newInstance(); 135 136 launch.invoke(o, doneSignal); 137 138 } catch (Throwable e) { 139 launchFailure = e; 140 } finally { 141 launchSignal.countDown(); 142 } 143 } 144 } 145 146 private static class MyClassLoader extends URLClassLoader { 147 148 private static boolean verbose = 149 Boolean.getBoolean("verboseClassLoading"); 150 private String uniqClassName; 151 152 public MyClassLoader(URL[] urls, String uniq) { 153 super(urls); 154 155 uniqClassName = uniq; 156 } 157 158 public Class loadClass(String name) throws ClassNotFoundException { 159 if (verbose) { 160 System.out.printf("%s: load class %s\n", uniqClassName, name); 161 } 162 if (uniqClassName.equals(name)) { 163 return Object.class; 164 } 165 return super.loadClass(name); 166 } 167 168 public String toString() { 169 return "MyClassLoader(" + uniqClassName + ")"; 170 } 171 } 172 173 private static void waitAndGC(int sec) { 174 int cnt = sec; 175 System.out.print("Wait "); 176 while (cnt-- > 0) { 177 try { 178 Thread.sleep(1000); 179 } catch (InterruptedException e) { 180 } 181 // do GC every 3 seconds 182 if (cnt % 3 == 2) { 183 System.gc(); 184 System.out.print("+"); 185 } else { 186 System.out.print("."); 187 } 188 //checkErrors(); 189 } 190 System.out.println(""); 191 } 192} 193 194abstract class AppTest { 195 196 public AppTest() { 197 } 198 199 protected abstract void doTest(); 200 201 public void launch(CountDownLatch done) { 202 System.out.println("Testcase: " + this.getClass().getName()); 203 try { 204 doTest(); 205 } finally { 206 done.countDown(); 207 } 208 } 209} 210 211class FontManagerTest extends AppTest { 212 213 public FontManagerTest() { 214 } 215 216 protected void doTest() { 217 Font f = new Font(Font.SANS_SERIF, Font.ITALIC, 24); 218 f.getNumGlyphs(); 219 } 220} 221