1/* 2 * Copyright (c) 2009, 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 24package test; 25 26import java.io.File; 27import java.io.IOException; 28import java.lang.ref.Reference; 29import java.lang.ref.ReferenceQueue; 30import java.lang.ref.WeakReference; 31import java.lang.reflect.Constructor; 32import java.lang.reflect.Method; 33import java.net.URL; 34import java.net.URLClassLoader; 35import java.util.HashMap; 36import java.util.Map.Entry; 37import java.util.Set; 38import java.util.WeakHashMap; 39import java.util.concurrent.ConcurrentLinkedQueue; 40import java.util.concurrent.CountDownLatch; 41import javax.imageio.stream.ImageInputStream; 42import sun.awt.AppContext; 43import sun.awt.SunToolkit; 44 45public class Main { 46 47 private static ThreadGroup appsThreadGroup; 48 49 private static WeakHashMap<MyClassLoader, String> refs = 50 new WeakHashMap<MyClassLoader, String>(); 51 52 /** Collection to simulate forgrotten streams **/ 53 private static HashMap<String, ImageInputStream> strongRefs = 54 new HashMap<String, ImageInputStream>(); 55 56 private static ConcurrentLinkedQueue<Throwable> problems = 57 new ConcurrentLinkedQueue<Throwable>(); 58 59 private static AppContext mainAppContext = null; 60 61 private static CountDownLatch doneSignal; 62 63 private static final int gcTimeout = 64 Integer.getInteger("gcTimeout", 10).intValue(); 65 66 private static boolean forgetSomeStreams = 67 Boolean.getBoolean("forgetSomeStreams"); 68 69 public static void main(String[] args) throws IOException { 70 mainAppContext = SunToolkit.createNewAppContext(); 71 System.out.println("Current context class loader: " + 72 Thread.currentThread().getContextClassLoader()); 73 74 appsThreadGroup = new ThreadGroup("MyAppsThreadGroup"); 75 76 File jar = new File("TestApp.jar"); 77 if (!jar.exists()) { 78 System.out.println(jar.getAbsolutePath() + " was not found!\n" + 79 "Please install the jar with test application correctly!"); 80 throw new RuntimeException("Test failed: no TestApp.jar"); 81 } 82 83 URL[] urls = new URL[]{jar.toURL()}; 84 85 int numApps = Integer.getInteger("numApps", 20).intValue(); 86 87 doneSignal = new CountDownLatch(numApps); 88 int cnt = 0; 89 while (cnt++ < numApps) { 90 launch(urls, "testapp.Main", "launch"); 91 92 checkErrors(); 93 } 94 95 System.out.println("Wait for apps completion...."); 96 97 try { 98 doneSignal.await(); 99 } catch (InterruptedException e) { 100 } 101 102 System.out.println("All apps finished."); 103 104 System.gc(); 105 106 System.out.flush(); 107 108 System.out.println("Enumerate strong refs:"); 109 for (String is : strongRefs.keySet()) { 110 System.out.println("-> " + is); 111 } 112 113 System.out.println("======================="); 114 115 // wait few seconds 116 waitAndGC(gcTimeout); 117 118 doneSignal = new CountDownLatch(1); 119 120 Runnable workaround = new Runnable() { 121 122 public void run() { 123 AppContext ctx = null; 124 try { 125 ctx = SunToolkit.createNewAppContext(); 126 } catch (Throwable e) { 127 // ignore... 128 } finally { 129 doneSignal.countDown(); 130 } 131 } 132 }; 133 134 Thread wt = new Thread(appsThreadGroup, workaround, "Workaround"); 135 wt.setContextClassLoader(new MyClassLoader(urls, "workaround")); 136 wt.start(); 137 wt = null; 138 workaround = null; 139 140 System.out.println("Wait for workaround completion..."); 141 142 try { 143 doneSignal.await(); 144 } catch (InterruptedException e) { 145 } 146 147 // give a chance to GC 148 waitAndGC(gcTimeout); 149 150 if (!refs.isEmpty()) { 151 System.out.println("Classloaders still alive:"); 152 153 for (MyClassLoader l : refs.keySet()) { 154 String val = refs.get(l); 155 156 if (val == null) { 157 throw new RuntimeException("Test FAILED: Invalid classloader name"); 158 } 159 System.out.println("->" + val + (strongRefs.get(val) != null ? 160 " (has strong ref)" : "")); 161 if (strongRefs.get(val) == null) { 162 throw new RuntimeException("Test FAILED: exta class loader is detected! "); 163 } 164 } 165 } else { 166 System.out.println("No alive class loaders!!"); 167 } 168 System.out.println("Test PASSED."); 169 } 170 171 private static void waitAndGC(int sec) { 172 int cnt = sec; 173 System.out.print("Wait "); 174 while (cnt-- > 0) { 175 try { 176 Thread.sleep(1000); 177 } catch (InterruptedException e) { 178 } 179 // do GC every 3 seconds 180 if (cnt % 3 == 2) { 181 System.gc(); 182 System.out.print("+"); 183 } else { 184 System.out.print("."); 185 } 186 checkErrors(); 187 } 188 System.out.println(""); 189 } 190 191 private static void checkErrors() { 192 while (!problems.isEmpty()) { 193 Throwable theProblem = problems.poll(); 194 System.out.println("Test FAILED!"); 195 do { 196 theProblem.printStackTrace(System.out); 197 theProblem = theProblem.getCause(); 198 } while (theProblem != null); 199 throw new RuntimeException("Test FAILED"); 200 } 201 } 202 static int counter = 0; 203 204 private static void launch(URL[] urls, final String className, 205 final String methodName) 206 { 207 final String uniqClassName = "testapp/Uniq" + counter; 208 final boolean saveStrongRef = forgetSomeStreams ? (counter % 5 == 4) : false; 209 210 System.out.printf("%s: launch the app\n", uniqClassName); 211 Runnable launchIt = new Runnable() { 212 public void run() { 213 AppContext ctx = SunToolkit.createNewAppContext(); 214 215 try { 216 Class appMain = 217 ctx.getContextClassLoader().loadClass(className); 218 Method launch = appMain.getDeclaredMethod(methodName, 219 strongRefs.getClass()); 220 221 Constructor c = appMain.getConstructor(String.class, 222 problems.getClass()); 223 224 Object o = c.newInstance(uniqClassName, problems); 225 226 if (saveStrongRef) { 227 System.out.printf("%s: force strong ref\n", 228 uniqClassName); 229 launch.invoke(o, strongRefs); 230 } else { 231 HashMap<String, ImageInputStream> empty = null; 232 launch.invoke(o, empty); 233 } 234 235 ctx = null; 236 } catch (Throwable e) { 237 problems.add(e); 238 } finally { 239 doneSignal.countDown(); 240 } 241 } 242 }; 243 244 MyClassLoader appClassLoader = new MyClassLoader(urls, uniqClassName); 245 246 refs.put(appClassLoader, uniqClassName); 247 248 Thread appThread = new Thread(appsThreadGroup, launchIt, 249 "AppThread" + counter++); 250 appThread.setContextClassLoader(appClassLoader); 251 252 appThread.start(); 253 launchIt = null; 254 appThread = null; 255 appClassLoader = null; 256 } 257 258 private static class MyClassLoader extends URLClassLoader { 259 260 private static boolean verbose = 261 Boolean.getBoolean("verboseClassLoading"); 262 private String uniqClassName; 263 264 public MyClassLoader(URL[] urls, String uniq) { 265 super(urls); 266 267 uniqClassName = uniq; 268 } 269 270 public Class loadClass(String name) throws ClassNotFoundException { 271 if (verbose) { 272 System.out.printf("%s: load class %s\n", uniqClassName, name); 273 } 274 if (uniqClassName.equals(name)) { 275 return Object.class; 276 } 277 return super.loadClass(name); 278 } 279 280 public String toString() { 281 return "MyClassLoader(" + uniqClassName + ")"; 282 } 283 } 284} 285