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