1 /*
2  * Copyright (c) 2013, 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     8002070 8013382
27  * @summary Remove the stack search for a resource bundle Logger to use
28  * @author  Jim Gish
29  * @build  ResourceBundleSearchTest IndirectlyLoadABundle LoadItUp1 LoadItUp2 TwiceIndirectlyLoadABundle LoadItUp2Invoker
30  * @run main/othervm ResourceBundleSearchTest
31  */
32 import java.net.URL;
33 import java.net.URLClassLoader;
34 import java.nio.file.Paths;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.MissingResourceException;
39 import java.util.ResourceBundle;
40 import java.util.logging.Logger;
41 
42 /**
43  * This class tests various scenarios of loading resource bundles from
44  * java.util.logging.  Since jtreg uses the logging system, it is necessary to
45  * run these tests using othervm mode to ensure no interference from logging
46  * initialization by jtreg
47  */
48 public class ResourceBundleSearchTest {
49 
50     private static final boolean DEBUG = false;
51     private static final String LOGGER_PREFIX = "myLogger.";
52     private static int loggerNum = 0;
53     private static final String PROP_RB_NAME = "ClassPathTestBundle";
54     private static final String TCCL_TEST_BUNDLE = "ContextClassLoaderTestBundle";
55 
56     private static int numPass = 0;
57     private static int numFail = 0;
58     private static List<String> msgs = new ArrayList<>();
59 
60     // This test has been falling in timeout - so we're adding some
61     // time stamp here and there to help diagnose whether it's a
62     // simple system slowness or whether there's a deeper issue,
63     // like a deadlock. The timeout issue should be fixed now,
64     // but we leave the time stamps in case it reappears.
65     //
66     static final long stamp = System.currentTimeMillis();
getTimeStamp()67     private static String getTimeStamp() {
68         long time = System.currentTimeMillis();
69         long delta = time - stamp;
70         long min = delta/60000;
71         long sec = (delta - min * 60000) / 10000;
72         long msec = delta - min * 60000 - sec * 1000;
73         return (min == 0 ? "" : (min + " min. ")) +
74                (sec == 0 ? "" : (sec + " sec. ")) +
75                (msec == 0 ? "" : (msec + "ms."));
76     }
77 
main(String[] args)78     public static void main(String[] args) throws Throwable {
79         System.out.println("ResourceBundleSearchTest starting: "+getTimeStamp());
80         ResourceBundleSearchTest test = new ResourceBundleSearchTest();
81         try {
82             test.runTests();
83         } finally {
84             System.out.println("ResourceBundleSearchTest terminated: "+getTimeStamp());
85         }
86     }
87 
runTests()88     private void runTests() throws Throwable {
89         // ensure we are using en as the default Locale so we can find the resource
90         Locale.setDefault(Locale.ENGLISH);
91 
92         ClassLoader myClassLoader = ClassLoader.getSystemClassLoader();
93 
94         // Find out where we are running from so we can setup the URLClassLoader URL
95         String userDir = System.getProperty("user.dir");
96         String testDir = System.getProperty("test.src", userDir);
97 
98         URL[] urls = new URL[1];
99 
100         urls[0] = Paths.get(testDir, "resources").toUri().toURL();
101         URLClassLoader rbClassLoader = new URLClassLoader(urls);
102 
103         int testnb = 1;
104         System.out.println("ResourceBundleSearchTest starting test #"+(testnb++)+": "+getTimeStamp());
105         // Test 1 - can we find a Logger bundle from doing a stack search?
106         // We shouldn't be able to
107         assertFalse(testGetBundleFromStackSearch(), "1-testGetBundleFromStackSearch");
108 
109         System.out.println("ResourceBundleSearchTest starting test #"+(testnb++)+": "+getTimeStamp());
110         // Test 2 - can we find a Logger bundle off of the Thread context class
111         // loader? We should be able to.
112         assertTrue(testGetBundleFromTCCL(TCCL_TEST_BUNDLE, rbClassLoader),
113                    "2-testGetBundleFromTCCL");
114 
115         System.out.println("ResourceBundleSearchTest starting test #"+(testnb++)+": "+getTimeStamp());
116         // Test 3 - Can we find a Logger bundle from the classpath?  We should be
117         // able to.  We'll first check to make sure the setup is correct and
118         // it actually is on the classpath before checking whether logging
119         // can see it there.
120         if (isOnClassPath(PROP_RB_NAME, myClassLoader)) {
121             debug("We should be able to see " + PROP_RB_NAME + " on the classpath");
122             assertTrue(testGetBundleFromSystemClassLoader(PROP_RB_NAME),
123                        "3-testGetBundleFromSystemClassLoader");
124         } else {
125             throw new Exception("TEST SETUP FAILURE: Cannot see " + PROP_RB_NAME
126                                  + " on the classpath");
127         }
128 
129         System.out.println("ResourceBundleSearchTest starting test #"+(testnb++)+": "+getTimeStamp());
130         // Test 4 - we should be able to find a bundle from the caller's
131         // classloader, but only one level up.
132         assertTrue(testGetBundleFromCallersClassLoader(),
133                    "4-testGetBundleFromCallersClassLoader");
134 
135         System.out.println("ResourceBundleSearchTest starting test #"+(testnb++)+": "+getTimeStamp());
136         // Test 5 - this ensures that getAnonymousLogger(String rbName)
137         // can find the bundle from the caller's classloader
138         assertTrue(testGetAnonymousLogger(), "5-testGetAnonymousLogger");
139 
140         System.out.println("ResourceBundleSearchTest starting test #"+(testnb++)+": "+getTimeStamp());
141         // Test 6 - first call getLogger("myLogger").
142         // Then call getLogger("myLogger","bundleName") from a different ClassLoader
143         // Make sure we find the bundle
144         assertTrue(testGetBundleFromSecondCallersClassLoader(),
145                    "6-testGetBundleFromSecondCallersClassLoader");
146 
147         System.out.println("ResourceBundleSearchTest generating report: "+getTimeStamp());
148         report();
149     }
150 
report()151     private void report() throws Exception {
152         System.out.println("Num passed = " + numPass + " Num failed = " + numFail);
153         if (numFail > 0) {
154             // We only care about the messages if they were errors
155             for (String msg : msgs) {
156                 System.out.println(msg);
157             }
158             throw new Exception(numFail + " out of " + (numPass + numFail)
159                                  + " tests failed.");
160         }
161     }
162 
assertTrue(boolean testResult, String testName)163     public void assertTrue(boolean testResult, String testName) {
164         if (testResult) {
165             numPass++;
166             System.out.println("PASSED: " + testName);
167         } else {
168             numFail++;
169             System.out.println("FAILED: " + testName
170                                + " was supposed to return true but did NOT!");
171         }
172     }
173 
assertFalse(boolean testResult, String testName)174     public void assertFalse(boolean testResult, String testName) {
175         if (!testResult) {
176             numPass++;
177             System.out.println("PASSED: " + testName);
178         } else {
179             numFail++;
180             System.out.println("FAILED: " + testName
181                                + " was supposed to return false but did NOT!");
182         }
183     }
184 
testGetBundleFromStackSearch()185     public boolean testGetBundleFromStackSearch() throws Throwable {
186         // This should fail.  This was the old functionality to search up the
187         // caller's call stack
188         TwiceIndirectlyLoadABundle indirectLoader = new TwiceIndirectlyLoadABundle();
189         return indirectLoader.loadAndTest();
190     }
191 
testGetBundleFromCallersClassLoader()192     public boolean testGetBundleFromCallersClassLoader() throws Throwable {
193         // This should pass.  This exercises getting the bundle using the
194         // class loader of the caller (one level up)
195         IndirectlyLoadABundle indirectLoader = new IndirectlyLoadABundle();
196         return indirectLoader.loadAndTest();
197     }
198 
testGetBundleFromTCCL(String bundleName, ClassLoader setOnTCCL)199     public boolean testGetBundleFromTCCL(String bundleName,
200             ClassLoader setOnTCCL) throws InterruptedException {
201         // This should succeed.  We should be able to get the bundle from the
202         // thread context class loader
203         debug("Looking for " + bundleName + " using TCCL");
204         LoggingThread lr = new LoggingThread(bundleName, setOnTCCL);
205         lr.start();
206         try {
207             lr.join();
208         } catch (InterruptedException ex) {
209             throw ex;
210         }
211         msgs.add(lr.msg);
212         return lr.foundBundle;
213     }
214 
215     /*
216      * @param String bundleClass
217      * @param ClassLoader to use for search
218      * @return true iff bundleClass is on system classpath
219      */
isOnClassPath(String baseName, ClassLoader cl)220     public static boolean isOnClassPath(String baseName, ClassLoader cl) {
221         ResourceBundle rb = null;
222         try {
223             rb = ResourceBundle.getBundle(baseName, Locale.getDefault(), cl);
224             System.out.println("INFO: Found bundle " + baseName + " on " + cl);
225         } catch (MissingResourceException e) {
226             System.out.println("INFO: Could not find bundle " + baseName + " on " + cl);
227             return false;
228         }
229         return (rb != null);
230     }
231 
newLoggerName()232     private static String newLoggerName() {
233         // we need a new logger name every time we attempt to find a bundle via
234         // the Logger.getLogger call, so we'll simply tack on an integer which
235         // we increment each time this is called
236         loggerNum++;
237         return LOGGER_PREFIX + loggerNum;
238     }
239 
testGetBundleFromSystemClassLoader(String bundleName)240     public boolean testGetBundleFromSystemClassLoader(String bundleName) {
241         // this should succeed if the bundle is on the system classpath.
242         try {
243             Logger aLogger = Logger.getLogger(ResourceBundleSearchTest.newLoggerName(),
244                     bundleName);
245         } catch (MissingResourceException re) {
246             msgs.add("INFO: testGetBundleFromSystemClassLoader() did not find bundle "
247                      + bundleName);
248             return false;
249         }
250         msgs.add("INFO: testGetBundleFromSystemClassLoader() found the bundle "
251                  + bundleName);
252         return true;
253     }
254 
testGetAnonymousLogger()255     private boolean testGetAnonymousLogger() throws Throwable {
256         // This should pass.  This exercises getting the bundle using the
257         // class loader of the caller (one level up) when calling
258         // Logger.getAnonymousLogger(String rbName)
259         IndirectlyLoadABundle indirectLoader = new IndirectlyLoadABundle();
260         return indirectLoader.testGetAnonymousLogger();
261     }
262 
testGetBundleFromSecondCallersClassLoader()263     private boolean testGetBundleFromSecondCallersClassLoader() throws Throwable {
264         // This should pass.  This exercises getting the bundle using the
265         // class loader of the caller (one level up)
266         IndirectlyLoadABundle indirectLoader = new IndirectlyLoadABundle();
267         return indirectLoader.testGetLoggerGetLoggerWithBundle();
268     }
269 
270     public static class LoggingThread extends Thread {
271 
272         boolean foundBundle = false;
273         String msg = null;
274         ClassLoader clToSetOnTCCL = null;
275         String bundleName = null;
276 
LoggingThread(String bundleName)277         public LoggingThread(String bundleName) {
278             this.bundleName = bundleName;
279         }
280 
LoggingThread(String bundleName, ClassLoader setOnTCCL)281         public LoggingThread(String bundleName, ClassLoader setOnTCCL) {
282             this.clToSetOnTCCL = setOnTCCL;
283             this.bundleName = bundleName;
284         }
285 
run()286         public void run() {
287             boolean setTCCL = false;
288             try {
289                 if (clToSetOnTCCL != null) {
290                     Thread.currentThread().setContextClassLoader(clToSetOnTCCL);
291                     setTCCL = true;
292                 }
293                 // this should succeed if the bundle is on the system classpath.
294                 try {
295                     Logger aLogger = Logger.getLogger(ResourceBundleSearchTest.newLoggerName(),
296                                                       bundleName);
297                     msg = "INFO: LoggingThread.run() found the bundle " + bundleName
298                           + (setTCCL ? " with " : " without ") + "setting the TCCL";
299                     foundBundle = true;
300                 } catch (MissingResourceException re) {
301                     msg = "INFO: LoggingThread.run() did not find the bundle " + bundleName
302                           + (setTCCL ? " with " : " without ") + "setting the TCCL";
303                     foundBundle = false;
304                 }
305             } catch (Throwable e) {
306                 e.printStackTrace();
307                 System.exit(1);
308             }
309         }
310     }
311 
debug(String msg)312     private void debug(String msg) {
313         if (DEBUG) {
314             System.out.println(msg);
315         }
316     }
317 }
318