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