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