src/share/classes/java/util/logging/Logger.java

Print this page
rev 6668 : 8002070: Remove the stack search for a resource bundle for Logger to use
Summary: The fragile, vulnerable, stack crawling has been eliminated from findResourceBundle(String)
Reviewed-by: mchung, alanb

*** 1,7 **** /* ! * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 24,37 **** */ package java.util.logging; - import java.util.*; - import java.util.concurrent.CopyOnWriteArrayList; - import java.security.*; import java.lang.ref.WeakReference; import java.util.function.Supplier; /** * A Logger object is used to log messages for a specific * system or application component. Loggers are normally named, --- 24,40 ---- */ package java.util.logging; import java.lang.ref.WeakReference; + import java.util.ArrayList; + import java.util.Iterator; + import java.util.Locale; + import java.util.MissingResourceException; + import java.util.ResourceBundle; + import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Supplier; /** * A Logger object is used to log messages for a specific * system or application component. Loggers are normally named,
*** 102,141 **** * which is invoked to construct the desired log message only when the message * actually is to be logged based on the effective log level thus eliminating * unnecessary message construction. For example, if the developer wants to * log system health status for diagnosis, with the String-accepting version, * the code would look like: ! <code><pre> class DiagnosisMessages { static String systemHealthStatus() { // collect system health information ... } } ... logger.log(Level.FINER, DiagnosisMessages.systemHealthStatus()); ! </pre></code> * With the above code, the health status is collected unnecessarily even when * the log level FINER is disabled. With the Supplier-accepting version as * below, the status will only be collected when the log level FINER is * enabled. ! <code><pre> logger.log(Level.FINER, DiagnosisMessages::systemHealthStatus); ! </pre></code> * <p> * When mapping ResourceBundle names to ResourceBundles, the Logger * will first try to use the Thread's ContextClassLoader. If that ! * is null it will try the SystemClassLoader instead. As a temporary ! * transition feature in the initial implementation, if the Logger is ! * unable to locate a ResourceBundle from the ContextClassLoader or ! * SystemClassLoader the Logger will also search up the class stack ! * and use successive calling ClassLoaders to try to locate a ResourceBundle. ! * (This call stack search is to allow containers to transition to ! * using ContextClassLoaders and is likely to be removed in future ! * versions.) * <p> * Formatting (including localization) is the responsibility of * the output Handler, which will typically call a Formatter. * <p> * Note that formatting need not occur synchronously. It may be delayed --- 105,138 ---- * which is invoked to construct the desired log message only when the message * actually is to be logged based on the effective log level thus eliminating * unnecessary message construction. For example, if the developer wants to * log system health status for diagnosis, with the String-accepting version, * the code would look like: ! <pre><code> class DiagnosisMessages { static String systemHealthStatus() { // collect system health information ... } } ... logger.log(Level.FINER, DiagnosisMessages.systemHealthStatus()); ! </code></pre> * With the above code, the health status is collected unnecessarily even when * the log level FINER is disabled. With the Supplier-accepting version as * below, the status will only be collected when the log level FINER is * enabled. ! <pre><code> logger.log(Level.FINER, DiagnosisMessages::systemHealthStatus); ! </code></pre> * <p> * When mapping ResourceBundle names to ResourceBundles, the Logger * will first try to use the Thread's ContextClassLoader. If that ! * is null it will try the ! * {@linkplain java.lang.ClassLoader#getSystemClassLoader() system ClassLoader} instead. * <p> * Formatting (including localization) is the responsibility of * the output Handler, which will typically call a Formatter. * <p> * Note that formatting need not occur synchronously. It may be delayed
*** 1539,1620 **** */ public boolean getUseParentHandlers() { return useParentHandlers; } ! // Private utility method to map a resource bundle name to an ! // actual resource bundle, using a simple one-entry cache. ! // Returns null for a null name. ! // May also return null if we can't find the resource bundle and ! // there is no suitable previous cached value. ! private synchronized ResourceBundle findResourceBundle(String name) { // Return a null bundle for a null name. if (name == null) { return null; } Locale currentLocale = Locale.getDefault(); // Normally we should hit on our simple one entry cache. ! if (catalog != null && currentLocale == catalogLocale ! && name == catalogName) { return catalog; } ! // Use the thread's context ClassLoader. If there isn't one, ! // use the SystemClassloader. ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } try { catalog = ResourceBundle.getBundle(name, currentLocale, cl); catalogName = name; catalogLocale = currentLocale; return catalog; } catch (MissingResourceException ex) { - // Woops. We can't find the ResourceBundle in the default - // ClassLoader. Drop through. - } - - - // Fall back to searching up the call stack and trying each - // calling ClassLoader. - for (int ix = 0; ; ix++) { - Class clz = sun.reflect.Reflection.getCallerClass(ix); - if (clz == null) { - break; - } - ClassLoader cl2 = clz.getClassLoader(); - if (cl2 == null) { - cl2 = ClassLoader.getSystemClassLoader(); - } - if (cl == cl2) { - // We've already checked this classloader. - continue; - } - cl = cl2; - try { - catalog = ResourceBundle.getBundle(name, currentLocale, cl); - catalogName = name; - catalogLocale = currentLocale; - return catalog; - } catch (MissingResourceException ex) { - // Ok, this one didn't work either. - // Drop through, and try the next one. - } - } - - if (name.equals(catalogName)) { - // Return the previous cached value for that name. - // This may be null. - return catalog; - } - // Sorry, we're out of luck. return null; } // Private utility method to initialize our one entry // resource bundle name cache. // Note: for consistency reasons, we are careful to check // that a suitable ResourceBundle exists before setting the --- 1536,1584 ---- */ public boolean getUseParentHandlers() { return useParentHandlers; } ! /** ! * Private utility method to map a resource bundle name to an ! * actual resource bundle, using a simple one-entry cache. ! * Returns null for a null name. ! * May also return null if we can't find the resource bundle and ! * there is no suitable previous cached value. ! * ! * @param name the ResourceBundle to locate ! * @return ResourceBundle specified by name or null if not found ! */ private synchronized ResourceBundle findResourceBundle(String name) { // Return a null bundle for a null name. if (name == null) { return null; } Locale currentLocale = Locale.getDefault(); // Normally we should hit on our simple one entry cache. ! if (catalog != null && currentLocale.equals(catalogLocale) ! && name.equals(catalogName)) { return catalog; } ! // Use the thread's context ClassLoader. If there isn't one, use the ! // {@linkplain java.lang.ClassLoader#getSystemClassLoader() system ClassLoader}. ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } try { catalog = ResourceBundle.getBundle(name, currentLocale, cl); catalogName = name; catalogLocale = currentLocale; return catalog; } catch (MissingResourceException ex) { return null; } + } // Private utility method to initialize our one entry // resource bundle name cache. // Note: for consistency reasons, we are careful to check // that a suitable ResourceBundle exists before setting the
*** 1636,1647 **** // cannot change ResourceBundles once they are set throw new IllegalArgumentException( resourceBundleName + " != " + name); } ! ResourceBundle rb = findResourceBundle(name); ! if (rb == null) { // We've failed to find an expected ResourceBundle. throw new MissingResourceException("Can't find " + name + " bundle", name, ""); } resourceBundleName = name; } --- 1600,1610 ---- // cannot change ResourceBundles once they are set throw new IllegalArgumentException( resourceBundleName + " != " + name); } ! if (findResourceBundle(name) == null) { // We've failed to find an expected ResourceBundle. throw new MissingResourceException("Can't find " + name + " bundle", name, ""); } resourceBundleName = name; }