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 /*
   2  * Copyright (c) 2000, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package java.util.logging;
  28 
  29 import java.util.*;
  30 import java.util.concurrent.CopyOnWriteArrayList;
  31 import java.security.*;
  32 import java.lang.ref.WeakReference;






  33 import java.util.function.Supplier;
  34 
  35 /**
  36  * A Logger object is used to log messages for a specific
  37  * system or application component.  Loggers are normally named,
  38  * using a hierarchical dot-separated namespace.  Logger names
  39  * can be arbitrary strings, but they should normally be based on
  40  * the package name or class name of the logged component, such
  41  * as java.net or javax.swing.  In addition it is possible to create
  42  * "anonymous" Loggers that are not stored in the Logger namespace.
  43  * <p>
  44  * Logger objects may be obtained by calls on one of the getLogger
  45  * factory methods.  These will either create a new Logger or
  46  * return a suitable existing Logger. It is important to note that
  47  * the Logger returned by one of the {@code getLogger} factory methods
  48  * may be garbage collected at any time if a strong reference to the
  49  * Logger is not kept.
  50  * <p>
  51  * Logging messages will be forwarded to registered Handler
  52  * objects, which can forward the messages to a variety of


  87  * it will inherit the ResourceBundle name from its parent,
  88  * recursively up the tree.
  89  * <p>
  90  * Most of the logger output methods take a "msg" argument.  This
  91  * msg argument may be either a raw value or a localization key.
  92  * During formatting, if the logger has (or inherits) a localization
  93  * ResourceBundle and if the ResourceBundle has a mapping for the msg
  94  * string, then the msg string is replaced by the localized value.
  95  * Otherwise the original msg string is used.  Typically, formatters use
  96  * java.text.MessageFormat style formatting to format parameters, so
  97  * for example a format string "{0} {1}" would format two parameters
  98  * as strings.
  99  * <p>
 100  * A set of methods alternatively take a "msgSupplier" instead of a "msg"
 101  * argument.  These methods take a {@link Supplier}{@code <String>} function
 102  * which is invoked to construct the desired log message only when the message
 103  * actually is to be logged based on the effective log level thus eliminating
 104  * unnecessary message construction. For example, if the developer wants to
 105  * log system health status for diagnosis, with the String-accepting version,
 106  * the code would look like:
 107  <code><pre>
 108 
 109    class DiagnosisMessages {
 110      static String systemHealthStatus() {
 111        // collect system health information
 112        ...
 113      }
 114    }
 115    ...
 116    logger.log(Level.FINER, DiagnosisMessages.systemHealthStatus());
 117  </pre></code>
 118  * With the above code, the health status is collected unnecessarily even when
 119  * the log level FINER is disabled. With the Supplier-accepting version as
 120  * below, the status will only be collected when the log level FINER is
 121  * enabled.
 122  <code><pre>
 123 
 124    logger.log(Level.FINER, DiagnosisMessages::systemHealthStatus);
 125  </pre></code>
 126  * <p>
 127  * When mapping ResourceBundle names to ResourceBundles, the Logger
 128  * will first try to use the Thread's ContextClassLoader.  If that
 129  * is null it will try the SystemClassLoader instead.  As a temporary
 130  * transition feature in the initial implementation, if the Logger is
 131  * unable to locate a ResourceBundle from the ContextClassLoader or
 132  * SystemClassLoader the Logger will also search up the class stack
 133  * and use successive calling ClassLoaders to try to locate a ResourceBundle.
 134  * (This call stack search is to allow containers to transition to
 135  * using ContextClassLoaders and is likely to be removed in future
 136  * versions.)
 137  * <p>
 138  * Formatting (including localization) is the responsibility of
 139  * the output Handler, which will typically call a Formatter.
 140  * <p>
 141  * Note that formatting need not occur synchronously.  It may be delayed
 142  * until a LogRecord is actually written to an external sink.
 143  * <p>
 144  * The logging methods are grouped in five main categories:
 145  * <ul>
 146  * <li><p>
 147  *     There are a set of "log" methods that take a log level, a message
 148  *     string, and optionally some parameters to the message string.
 149  * <li><p>
 150  *     There are a set of "logp" methods (for "log precise") that are
 151  *     like the "log" methods, but also take an explicit source class name
 152  *     and method name.
 153  * <li><p>
 154  *     There are a set of "logrb" method (for "log with resource bundle")
 155  *     that are like the "logp" method, but also take an explicit resource
 156  *     bundle name for use in localizing the log message.


1524      * @param useParentHandlers   true if output is to be sent to the
1525      *          logger's parent.
1526      * @exception  SecurityException  if a security manager exists and if
1527      *             the caller does not have LoggingPermission("control").
1528      */
1529     public void setUseParentHandlers(boolean useParentHandlers) {
1530         checkPermission();
1531         this.useParentHandlers = useParentHandlers;
1532     }
1533 
1534     /**
1535      * Discover whether or not this logger is sending its output
1536      * to its parent logger.
1537      *
1538      * @return  true if output is to be sent to the logger's parent
1539      */
1540     public boolean getUseParentHandlers() {
1541         return useParentHandlers;
1542     }
1543 
1544     // Private utility method to map a resource bundle name to an
1545     // actual resource bundle, using a simple one-entry cache.
1546     // Returns null for a null name.
1547     // May also return null if we can't find the resource bundle and
1548     // there is no suitable previous cached value.
1549 




1550     private synchronized ResourceBundle findResourceBundle(String name) {
1551         // Return a null bundle for a null name.
1552         if (name == null) {
1553             return null;
1554         }
1555 
1556         Locale currentLocale = Locale.getDefault();
1557 
1558         // Normally we should hit on our simple one entry cache.
1559         if (catalog != null && currentLocale == catalogLocale
1560                                         && name == catalogName) {
1561             return catalog;
1562         }
1563 
1564         // Use the thread's context ClassLoader.  If there isn't one,
1565         // use the SystemClassloader.
1566         ClassLoader cl = Thread.currentThread().getContextClassLoader();
1567         if (cl == null) {
1568             cl = ClassLoader.getSystemClassLoader();
1569         }
1570         try {
1571             catalog = ResourceBundle.getBundle(name, currentLocale, cl);
1572             catalogName = name;
1573             catalogLocale = currentLocale;
1574             return catalog;
1575         } catch (MissingResourceException ex) {
1576             // Woops.  We can't find the ResourceBundle in the default
1577             // ClassLoader.  Drop through.
1578         }
1579 
1580 
1581         // Fall back to searching up the call stack and trying each
1582         // calling ClassLoader.
1583         for (int ix = 0; ; ix++) {
1584             Class clz = sun.reflect.Reflection.getCallerClass(ix);
1585             if (clz == null) {
1586                 break;
1587             }
1588             ClassLoader cl2 = clz.getClassLoader();
1589             if (cl2 == null) {
1590                 cl2 = ClassLoader.getSystemClassLoader();
1591             }
1592             if (cl == cl2) {
1593                 // We've already checked this classloader.
1594                 continue;
1595             }
1596             cl = cl2;
1597             try {
1598                 catalog = ResourceBundle.getBundle(name, currentLocale, cl);
1599                 catalogName = name;
1600                 catalogLocale = currentLocale;
1601                 return catalog;
1602             } catch (MissingResourceException ex) {
1603                 // Ok, this one didn't work either.
1604                 // Drop through, and try the next one.
1605             }
1606         }
1607 
1608         if (name.equals(catalogName)) {
1609             // Return the previous cached value for that name.
1610             // This may be null.
1611             return catalog;
1612         }
1613         // Sorry, we're out of luck.
1614         return null;
1615     }

1616 
1617     // Private utility method to initialize our one entry
1618     // resource bundle name cache.
1619     // Note: for consistency reasons, we are careful to check
1620     // that a suitable ResourceBundle exists before setting the
1621     // resourceBundleName field.
1622     // Synchronized to prevent races in setting the field.
1623     private synchronized void setupResourceInfo(String name) {
1624         if (name == null) {
1625             return;
1626         }
1627 
1628         if (resourceBundleName != null) {
1629             // this Logger already has a ResourceBundle
1630 
1631             if (resourceBundleName.equals(name)) {
1632                 // the names match so there is nothing more to do
1633                 return;
1634             }
1635 
1636             // cannot change ResourceBundles once they are set
1637             throw new IllegalArgumentException(
1638                 resourceBundleName + " != " + name);
1639         }
1640 
1641         ResourceBundle rb = findResourceBundle(name);
1642         if (rb == null) {
1643             // We've failed to find an expected ResourceBundle.
1644             throw new MissingResourceException("Can't find " + name + " bundle", name, "");
1645         }
1646         resourceBundleName = name;
1647     }
1648 
1649     /**
1650      * Return the parent for this Logger.
1651      * <p>
1652      * This method returns the nearest extant parent in the namespace.
1653      * Thus if a Logger is called "a.b.c.d", and a Logger called "a.b"
1654      * has been created but no logger "a.b.c" exists, then a call of
1655      * getParent on the Logger "a.b.c.d" will return the Logger "a.b".
1656      * <p>
1657      * The result will be null if it is called on the root Logger
1658      * in the namespace.
1659      *
1660      * @return nearest existing parent Logger
1661      */
1662     public Logger getParent() {


   1 /*
   2  * Copyright (c) 2000, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package java.util.logging;
  28 



  29 import java.lang.ref.WeakReference;
  30 import java.util.ArrayList;
  31 import java.util.Iterator;
  32 import java.util.Locale;
  33 import java.util.MissingResourceException;
  34 import java.util.ResourceBundle;
  35 import java.util.concurrent.CopyOnWriteArrayList;
  36 import java.util.function.Supplier;
  37 
  38 /**
  39  * A Logger object is used to log messages for a specific
  40  * system or application component.  Loggers are normally named,
  41  * using a hierarchical dot-separated namespace.  Logger names
  42  * can be arbitrary strings, but they should normally be based on
  43  * the package name or class name of the logged component, such
  44  * as java.net or javax.swing.  In addition it is possible to create
  45  * "anonymous" Loggers that are not stored in the Logger namespace.
  46  * <p>
  47  * Logger objects may be obtained by calls on one of the getLogger
  48  * factory methods.  These will either create a new Logger or
  49  * return a suitable existing Logger. It is important to note that
  50  * the Logger returned by one of the {@code getLogger} factory methods
  51  * may be garbage collected at any time if a strong reference to the
  52  * Logger is not kept.
  53  * <p>
  54  * Logging messages will be forwarded to registered Handler
  55  * objects, which can forward the messages to a variety of


  90  * it will inherit the ResourceBundle name from its parent,
  91  * recursively up the tree.
  92  * <p>
  93  * Most of the logger output methods take a "msg" argument.  This
  94  * msg argument may be either a raw value or a localization key.
  95  * During formatting, if the logger has (or inherits) a localization
  96  * ResourceBundle and if the ResourceBundle has a mapping for the msg
  97  * string, then the msg string is replaced by the localized value.
  98  * Otherwise the original msg string is used.  Typically, formatters use
  99  * java.text.MessageFormat style formatting to format parameters, so
 100  * for example a format string "{0} {1}" would format two parameters
 101  * as strings.
 102  * <p>
 103  * A set of methods alternatively take a "msgSupplier" instead of a "msg"
 104  * argument.  These methods take a {@link Supplier}{@code <String>} function
 105  * which is invoked to construct the desired log message only when the message
 106  * actually is to be logged based on the effective log level thus eliminating
 107  * unnecessary message construction. For example, if the developer wants to
 108  * log system health status for diagnosis, with the String-accepting version,
 109  * the code would look like:
 110  <pre><code>
 111 
 112    class DiagnosisMessages {
 113      static String systemHealthStatus() {
 114        // collect system health information
 115        ...
 116      }
 117    }
 118    ...
 119    logger.log(Level.FINER, DiagnosisMessages.systemHealthStatus());
 120 </code></pre>
 121  * With the above code, the health status is collected unnecessarily even when
 122  * the log level FINER is disabled. With the Supplier-accepting version as
 123  * below, the status will only be collected when the log level FINER is
 124  * enabled.
 125  <pre><code>
 126 
 127    logger.log(Level.FINER, DiagnosisMessages::systemHealthStatus);
 128 </code></pre>
 129  * <p>
 130  * When mapping ResourceBundle names to ResourceBundles, the Logger
 131  * will first try to use the Thread's ContextClassLoader.  If that
 132  * is null it will try the
 133  * {@linkplain java.lang.ClassLoader#getSystemClassLoader() system ClassLoader} instead.






 134  * <p>
 135  * Formatting (including localization) is the responsibility of
 136  * the output Handler, which will typically call a Formatter.
 137  * <p>
 138  * Note that formatting need not occur synchronously.  It may be delayed
 139  * until a LogRecord is actually written to an external sink.
 140  * <p>
 141  * The logging methods are grouped in five main categories:
 142  * <ul>
 143  * <li><p>
 144  *     There are a set of "log" methods that take a log level, a message
 145  *     string, and optionally some parameters to the message string.
 146  * <li><p>
 147  *     There are a set of "logp" methods (for "log precise") that are
 148  *     like the "log" methods, but also take an explicit source class name
 149  *     and method name.
 150  * <li><p>
 151  *     There are a set of "logrb" method (for "log with resource bundle")
 152  *     that are like the "logp" method, but also take an explicit resource
 153  *     bundle name for use in localizing the log message.


1521      * @param useParentHandlers   true if output is to be sent to the
1522      *          logger's parent.
1523      * @exception  SecurityException  if a security manager exists and if
1524      *             the caller does not have LoggingPermission("control").
1525      */
1526     public void setUseParentHandlers(boolean useParentHandlers) {
1527         checkPermission();
1528         this.useParentHandlers = useParentHandlers;
1529     }
1530 
1531     /**
1532      * Discover whether or not this logger is sending its output
1533      * to its parent logger.
1534      *
1535      * @return  true if output is to be sent to the logger's parent
1536      */
1537     public boolean getUseParentHandlers() {
1538         return useParentHandlers;
1539     }
1540 
1541     /**
1542      * Private utility method to map a resource bundle name to an
1543      * actual resource bundle, using a simple one-entry cache.
1544      * Returns null for a null name.
1545      * May also return null if we can't find the resource bundle and
1546      * there is no suitable previous cached value.
1547      *
1548      * @param name the ResourceBundle to locate
1549      * @return ResourceBundle specified by name or null if not found
1550      */
1551     private synchronized ResourceBundle findResourceBundle(String name) {
1552         // Return a null bundle for a null name.
1553         if (name == null) {
1554             return null;
1555         }
1556 
1557         Locale currentLocale = Locale.getDefault();
1558 
1559         // Normally we should hit on our simple one entry cache.
1560         if (catalog != null && currentLocale.equals(catalogLocale)
1561                 && name.equals(catalogName)) {
1562             return catalog;
1563         }
1564 
1565         // Use the thread's context ClassLoader.  If there isn't one, use the
1566         // {@linkplain java.lang.ClassLoader#getSystemClassLoader() system ClassLoader}.
1567         ClassLoader cl = Thread.currentThread().getContextClassLoader();
1568         if (cl == null) {
1569             cl = ClassLoader.getSystemClassLoader();
1570         }
1571         try {
1572             catalog = ResourceBundle.getBundle(name, currentLocale, cl);
1573             catalogName = name;
1574             catalogLocale = currentLocale;
1575             return catalog;
1576         } catch (MissingResourceException ex) {






































1577             return null;
1578         }
1579     }
1580 
1581     // Private utility method to initialize our one entry
1582     // resource bundle name cache.
1583     // Note: for consistency reasons, we are careful to check
1584     // that a suitable ResourceBundle exists before setting the
1585     // resourceBundleName field.
1586     // Synchronized to prevent races in setting the field.
1587     private synchronized void setupResourceInfo(String name) {
1588         if (name == null) {
1589             return;
1590         }
1591 
1592         if (resourceBundleName != null) {
1593             // this Logger already has a ResourceBundle
1594 
1595             if (resourceBundleName.equals(name)) {
1596                 // the names match so there is nothing more to do
1597                 return;
1598             }
1599 
1600             // cannot change ResourceBundles once they are set
1601             throw new IllegalArgumentException(
1602                 resourceBundleName + " != " + name);
1603         }
1604 
1605         if (findResourceBundle(name) == null) {

1606             // We've failed to find an expected ResourceBundle.
1607             throw new MissingResourceException("Can't find " + name + " bundle", name, "");
1608         }
1609         resourceBundleName = name;
1610     }
1611 
1612     /**
1613      * Return the parent for this Logger.
1614      * <p>
1615      * This method returns the nearest extant parent in the namespace.
1616      * Thus if a Logger is called "a.b.c.d", and a Logger called "a.b"
1617      * has been created but no logger "a.b.c" exists, then a call of
1618      * getParent on the Logger "a.b.c.d" will return the Logger "a.b".
1619      * <p>
1620      * The result will be null if it is called on the root Logger
1621      * in the namespace.
1622      *
1623      * @return nearest existing parent Logger
1624      */
1625     public Logger getParent() {