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 
  34 /**
  35  * A Logger object is used to log messages for a specific
  36  * system or application component.  Loggers are normally named,
  37  * using a hierarchical dot-separated namespace.  Logger names
  38  * can be arbitrary strings, but they should normally be based on
  39  * the package name or class name of the logged component, such
  40  * as java.net or javax.swing.  In addition it is possible to create
  41  * "anonymous" Loggers that are not stored in the Logger namespace.
  42  * <p>
  43  * Logger objects may be obtained by calls on one of the getLogger
  44  * factory methods.  These will either create a new Logger or
  45  * return a suitable existing Logger. It is important to note that
  46  * the Logger returned by one of the {@code getLogger} factory methods
  47  * may be garbage collected at any time if a strong reference to the
  48  * Logger is not kept.
  49  * <p>
  50  * Logging messages will be forwarded to registered Handler
  51  * objects, which can forward the messages to a variety of
  52  * destinations, including consoles, files, OS logs, etc.
  53  * <p>
  54  * Each Logger keeps track of a "parent" Logger, which is its
  55  * nearest existing ancestor in the Logger namespace.
  56  * <p>
  57  * Each Logger has a "Level" associated with it.  This reflects
  58  * a minimum Level that this logger cares about.  If a Logger's
  59  * level is set to <tt>null</tt>, then its effective level is inherited
  60  * from its parent, which may in turn obtain it recursively from its
  61  * parent, and so on up the tree.
  62  * <p>
  63  * The log level can be configured based on the properties from the
  64  * logging configuration file, as described in the description
  65  * of the LogManager class.  However it may also be dynamically changed
  66  * by calls on the Logger.setLevel method.  If a logger's level is
  67  * changed the change may also affect child loggers, since any child
  68  * logger that has <tt>null</tt> as its level will inherit its
  69  * effective level from its parent.
  70  * <p>
  71  * On each logging call the Logger initially performs a cheap
  72  * check of the request level (e.g., SEVERE or FINE) against the
  73  * effective log level of the logger.  If the request level is
  74  * lower than the log level, the logging call returns immediately.
  75  * <p>
  76  * After passing this initial (cheap) test, the Logger will allocate
  77  * a LogRecord to describe the logging message.  It will then call a
  78  * Filter (if present) to do a more detailed check on whether the
  79  * record should be published.  If that passes it will then publish
  80  * the LogRecord to its output Handlers.  By default, loggers also
  81  * publish to their parent's Handlers, recursively up the tree.
  82  * <p>
  83  * Each Logger may have a ResourceBundle name associated with it.
  84  * The named bundle will be used for localizing logging messages.
  85  * If a Logger does not have its own ResourceBundle name, then
  86  * it will inherit the ResourceBundle name from its parent,
  87  * recursively up the tree.
  88  * <p>
  89  * Most of the logger output methods take a "msg" argument.  This
  90  * msg argument may be either a raw value or a localization key.
  91  * During formatting, if the logger has (or inherits) a localization
  92  * ResourceBundle and if the ResourceBundle has a mapping for the msg
  93  * string, then the msg string is replaced by the localized value.
  94  * Otherwise the original msg string is used.  Typically, formatters use
  95  * java.text.MessageFormat style formatting to format parameters, so
  96  * for example a format string "{0} {1}" would format two parameters
  97  * as strings.
  98  * <p>
  99  * When mapping ResourceBundle names to ResourceBundles, the Logger
 100  * will first try to use the Thread's ContextClassLoader.  If that
 101  * is null it will try the SystemClassLoader instead.  As a temporary
 102  * transition feature in the initial implementation, if the Logger is
 103  * unable to locate a ResourceBundle from the ContextClassLoader or
 104  * SystemClassLoader the Logger will also search up the class stack
 105  * and use successive calling ClassLoaders to try to locate a ResourceBundle.
 106  * (This call stack search is to allow containers to transition to
 107  * using ContextClassLoaders and is likely to be removed in future
 108  * versions.)
 109  * <p>
 110  * Formatting (including localization) is the responsibility of
 111  * the output Handler, which will typically call a Formatter.
 112  * <p>
 113  * Note that formatting need not occur synchronously.  It may be delayed
 114  * until a LogRecord is actually written to an external sink.
 115  * <p>
 116  * The logging methods are grouped in five main categories:
 117  * <ul>
 118  * <li><p>
 119  *     There are a set of "log" methods that take a log level, a message
 120  *     string, and optionally some parameters to the message string.
 121  * <li><p>
 122  *     There are a set of "logp" methods (for "log precise") that are
 123  *     like the "log" methods, but also take an explicit source class name
 124  *     and method name.
 125  * <li><p>
 126  *     There are a set of "logrb" method (for "log with resource bundle")
 127  *     that are like the "logp" method, but also take an explicit resource
 128  *     bundle name for use in localizing the log message.
 129  * <li><p>
 130  *     There are convenience methods for tracing method entries (the
 131  *     "entering" methods), method returns (the "exiting" methods) and
 132  *     throwing exceptions (the "throwing" methods).
 133  * <li><p>
 134  *     Finally, there are a set of convenience methods for use in the
 135  *     very simplest cases, when a developer simply wants to log a
 136  *     simple string at a given log level.  These methods are named
 137  *     after the standard Level names ("severe", "warning", "info", etc.)
 138  *     and take a single argument, a message string.
 139  * </ul>
 140  * <p>
 141  * For the methods that do not take an explicit source name and
 142  * method name, the Logging framework will make a "best effort"
 143  * to determine which class and method called into the logging method.
 144  * However, it is important to realize that this automatically inferred
 145  * information may only be approximate (or may even be quite wrong!).
 146  * Virtual machines are allowed to do extensive optimizations when
 147  * JITing and may entirely remove stack frames, making it impossible
 148  * to reliably locate the calling class and method.
 149  * <P>
 150  * All methods on Logger are multi-thread safe.
 151  * <p>
 152  * <b>Subclassing Information:</b> Note that a LogManager class may
 153  * provide its own implementation of named Loggers for any point in
 154  * the namespace.  Therefore, any subclasses of Logger (unless they
 155  * are implemented in conjunction with a new LogManager class) should
 156  * take care to obtain a Logger instance from the LogManager class and
 157  * should delegate operations such as "isLoggable" and "log(LogRecord)"
 158  * to that instance.  Note that in order to intercept all logging
 159  * output, subclasses need only override the log(LogRecord) method.
 160  * All the other logging methods are implemented as calls on this
 161  * log(LogRecord) method.
 162  *
 163  * @since 1.4
 164  */
 165 
 166 
 167 public class Logger {
 168     private static final Handler emptyHandlers[] = new Handler[0];
 169     private static final int offValue = Level.OFF.intValue();
 170     private LogManager manager;
 171     private String name;
 172     private final CopyOnWriteArrayList<Handler> handlers =
 173         new CopyOnWriteArrayList<>();
 174     private String resourceBundleName;
 175     private volatile boolean useParentHandlers = true;
 176     private volatile Filter filter;
 177     private boolean anonymous;
 178 
 179     private ResourceBundle catalog;     // Cached resource bundle
 180     private String catalogName;         // name associated with catalog
 181     private Locale catalogLocale;       // locale associated with catalog
 182 
 183     // The fields relating to parent-child relationships and levels
 184     // are managed under a separate lock, the treeLock.
 185     private static Object treeLock = new Object();
 186     // We keep weak references from parents to children, but strong
 187     // references from children to parents.
 188     private volatile Logger parent;    // our nearest parent.
 189     private ArrayList<LogManager.LoggerWeakRef> kids;   // WeakReferences to loggers that have us as parent
 190     private volatile Level levelObject;
 191     private volatile int levelValue;  // current effective level value
 192 
 193     /**
 194      * GLOBAL_LOGGER_NAME is a name for the global logger.
 195      *
 196      * @since 1.6
 197      */
 198     public static final String GLOBAL_LOGGER_NAME = "global";
 199 
 200     /**
 201      * Return global logger object with the name Logger.GLOBAL_LOGGER_NAME.
 202      *
 203      * @return global logger object
 204      * @since 1.7
 205      */
 206     public static final Logger getGlobal() {
 207         return global;
 208     }
 209 
 210     /**
 211      * The "global" Logger object is provided as a convenience to developers
 212      * who are making casual use of the Logging package.  Developers
 213      * who are making serious use of the logging package (for example
 214      * in products) should create and use their own Logger objects,
 215      * with appropriate names, so that logging can be controlled on a
 216      * suitable per-Logger granularity. Developers also need to keep a
 217      * strong reference to their Logger objects to prevent them from
 218      * being garbage collected.
 219      * <p>
 220      * @deprecated Initialization of this field is prone to deadlocks.
 221      * The field must be initialized by the Logger class initialization
 222      * which may cause deadlocks with the LogManager class initialization.
 223      * In such cases two class initialization wait for each other to complete.
 224      * The preferred way to get the global logger object is via the call
 225      * <code>Logger.getGlobal()</code>.
 226      * For compatibility with old JDK versions where the
 227      * <code>Logger.getGlobal()</code> is not available use the call
 228      * <code>Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)</code>
 229      * or <code>Logger.getLogger("global")</code>.
 230      */
 231     @Deprecated
 232     public static final Logger global = new Logger(GLOBAL_LOGGER_NAME);
 233 
 234     /**
 235      * Protected method to construct a logger for a named subsystem.
 236      * <p>
 237      * The logger will be initially configured with a null Level
 238      * and with useParentHandlers set to true.
 239      *
 240      * @param   name    A name for the logger.  This should
 241      *                          be a dot-separated name and should normally
 242      *                          be based on the package name or class name
 243      *                          of the subsystem, such as java.net
 244      *                          or javax.swing.  It may be null for anonymous Loggers.
 245      * @param   resourceBundleName  name of ResourceBundle to be used for localizing
 246      *                          messages for this logger.  May be null if none
 247      *                          of the messages require localization.
 248      * @throws MissingResourceException if the resourceBundleName is non-null and
 249      *             no corresponding resource can be found.
 250      */
 251     protected Logger(String name, String resourceBundleName) {
 252         this.manager = LogManager.getLogManager();
 253         if (resourceBundleName != null) {
 254             // MissingResourceException or IllegalArgumentException can
 255             // be thrown by setupResourceInfo(). Since this is the Logger
 256             // constructor, the resourceBundleName field is null so
 257             // IllegalArgumentException cannot happen here.
 258             setupResourceInfo(resourceBundleName);
 259         }
 260         this.name = name;
 261         levelValue = Level.INFO.intValue();
 262     }
 263 
 264     // This constructor is used only to create the global Logger.
 265     // It is needed to break a cyclic dependence between the LogManager
 266     // and Logger static initializers causing deadlocks.
 267     private Logger(String name) {
 268         // The manager field is not initialized here.
 269         this.name = name;
 270         levelValue = Level.INFO.intValue();
 271     }
 272 
 273     // It is called from the LogManager.<clinit> to complete
 274     // initialization of the global Logger.
 275     void setLogManager(LogManager manager) {
 276         this.manager = manager;
 277     }
 278 
 279     private void checkPermission() throws SecurityException {
 280         if (!anonymous) {
 281             if (manager == null) {
 282                 // Complete initialization of the global Logger.
 283                 manager = LogManager.getLogManager();
 284             }
 285             manager.checkPermission();
 286         }
 287     }
 288 
 289     /**
 290      * Find or create a logger for a named subsystem.  If a logger has
 291      * already been created with the given name it is returned.  Otherwise
 292      * a new logger is created.
 293      * <p>
 294      * If a new logger is created its log level will be configured
 295      * based on the LogManager configuration and it will configured
 296      * to also send logging output to its parent's Handlers.  It will
 297      * be registered in the LogManager global namespace.
 298      * <p>
 299      * Note: The LogManager may only retain a weak reference to the newly
 300      * created Logger. It is important to understand that a previously
 301      * created Logger with the given name may be garbage collected at any
 302      * time if there is no strong reference to the Logger. In particular,
 303      * this means that two back-to-back calls like
 304      * {@code getLogger("MyLogger").log(...)} may use different Logger
 305      * objects named "MyLogger" if there is no strong reference to the
 306      * Logger named "MyLogger" elsewhere in the program.
 307      *
 308      * @param   name            A name for the logger.  This should
 309      *                          be a dot-separated name and should normally
 310      *                          be based on the package name or class name
 311      *                          of the subsystem, such as java.net
 312      *                          or javax.swing
 313      * @return a suitable Logger
 314      * @throws NullPointerException if the name is null.
 315      */
 316 
 317     // Synchronization is not required here. All synchronization for
 318     // adding a new Logger object is handled by LogManager.addLogger().
 319     public static Logger getLogger(String name) {
 320         // This method is intentionally not a wrapper around a call
 321         // to getLogger(name, resourceBundleName). If it were then
 322         // this sequence:
 323         //
 324         //     getLogger("Foo", "resourceBundleForFoo");
 325         //     getLogger("Foo");
 326         //
 327         // would throw an IllegalArgumentException in the second call
 328         // because the wrapper would result in an attempt to replace
 329         // the existing "resourceBundleForFoo" with null.
 330         LogManager manager = LogManager.getLogManager();
 331         return manager.demandLogger(name);
 332     }
 333 
 334     /**
 335      * Find or create a logger for a named subsystem.  If a logger has
 336      * already been created with the given name it is returned.  Otherwise
 337      * a new logger is created.
 338      * <p>
 339      * If a new logger is created its log level will be configured
 340      * based on the LogManager and it will configured to also send logging
 341      * output to its parent's Handlers.  It will be registered in
 342      * the LogManager global namespace.
 343      * <p>
 344      * Note: The LogManager may only retain a weak reference to the newly
 345      * created Logger. It is important to understand that a previously
 346      * created Logger with the given name may be garbage collected at any
 347      * time if there is no strong reference to the Logger. In particular,
 348      * this means that two back-to-back calls like
 349      * {@code getLogger("MyLogger", ...).log(...)} may use different Logger
 350      * objects named "MyLogger" if there is no strong reference to the
 351      * Logger named "MyLogger" elsewhere in the program.
 352      * <p>
 353      * If the named Logger already exists and does not yet have a
 354      * localization resource bundle then the given resource bundle
 355      * name is used.  If the named Logger already exists and has
 356      * a different resource bundle name then an IllegalArgumentException
 357      * is thrown.
 358      * <p>
 359      * @param   name    A name for the logger.  This should
 360      *                          be a dot-separated name and should normally
 361      *                          be based on the package name or class name
 362      *                          of the subsystem, such as java.net
 363      *                          or javax.swing
 364      * @param   resourceBundleName  name of ResourceBundle to be used for localizing
 365      *                          messages for this logger. May be <CODE>null</CODE> if none of
 366      *                          the messages require localization.
 367      * @return a suitable Logger
 368      * @throws MissingResourceException if the resourceBundleName is non-null and
 369      *             no corresponding resource can be found.
 370      * @throws IllegalArgumentException if the Logger already exists and uses
 371      *             a different resource bundle name.
 372      * @throws NullPointerException if the name is null.
 373      */
 374 
 375     // Synchronization is not required here. All synchronization for
 376     // adding a new Logger object is handled by LogManager.addLogger().
 377     public static Logger getLogger(String name, String resourceBundleName) {
 378         LogManager manager = LogManager.getLogManager();
 379         Logger result = manager.demandLogger(name);
 380 
 381         // MissingResourceException or IllegalArgumentException can be
 382         // thrown by setupResourceInfo().
 383         result.setupResourceInfo(resourceBundleName);
 384         return result;
 385     }
 386 
 387 
 388     /**
 389      * Create an anonymous Logger.  The newly created Logger is not
 390      * registered in the LogManager namespace.  There will be no
 391      * access checks on updates to the logger.
 392      * <p>
 393      * This factory method is primarily intended for use from applets.
 394      * Because the resulting Logger is anonymous it can be kept private
 395      * by the creating class.  This removes the need for normal security
 396      * checks, which in turn allows untrusted applet code to update
 397      * the control state of the Logger.  For example an applet can do
 398      * a setLevel or an addHandler on an anonymous Logger.
 399      * <p>
 400      * Even although the new logger is anonymous, it is configured
 401      * to have the root logger ("") as its parent.  This means that
 402      * by default it inherits its effective level and handlers
 403      * from the root logger.
 404      * <p>
 405      *
 406      * @return a newly created private Logger
 407      */
 408     public static Logger getAnonymousLogger() {
 409         return getAnonymousLogger(null);
 410     }
 411 
 412     /**
 413      * Create an anonymous Logger.  The newly created Logger is not
 414      * registered in the LogManager namespace.  There will be no
 415      * access checks on updates to the logger.
 416      * <p>
 417      * This factory method is primarily intended for use from applets.
 418      * Because the resulting Logger is anonymous it can be kept private
 419      * by the creating class.  This removes the need for normal security
 420      * checks, which in turn allows untrusted applet code to update
 421      * the control state of the Logger.  For example an applet can do
 422      * a setLevel or an addHandler on an anonymous Logger.
 423      * <p>
 424      * Even although the new logger is anonymous, it is configured
 425      * to have the root logger ("") as its parent.  This means that
 426      * by default it inherits its effective level and handlers
 427      * from the root logger.
 428      * <p>
 429      * @param   resourceBundleName  name of ResourceBundle to be used for localizing
 430      *                          messages for this logger.
 431      *          May be null if none of the messages require localization.
 432      * @return a newly created private Logger
 433      * @throws MissingResourceException if the resourceBundleName is non-null and
 434      *             no corresponding resource can be found.
 435      */
 436 
 437     // Synchronization is not required here. All synchronization for
 438     // adding a new anonymous Logger object is handled by doSetParent().
 439     public static Logger getAnonymousLogger(String resourceBundleName) {
 440         LogManager manager = LogManager.getLogManager();
 441         // cleanup some Loggers that have been GC'ed
 442         manager.drainLoggerRefQueueBounded();
 443         Logger result = new Logger(null, resourceBundleName);
 444         result.anonymous = true;
 445         Logger root = manager.getLogger("");
 446         result.doSetParent(root);
 447         return result;
 448     }
 449 
 450     /**
 451      * Retrieve the localization resource bundle for this
 452      * logger for the current default locale.  Note that if
 453      * the result is null, then the Logger will use a resource
 454      * bundle inherited from its parent.
 455      *
 456      * @return localization bundle (may be null)
 457      */
 458     public ResourceBundle getResourceBundle() {
 459         return findResourceBundle(getResourceBundleName());
 460     }
 461 
 462     /**
 463      * Retrieve the localization resource bundle name for this
 464      * logger.  Note that if the result is null, then the Logger
 465      * will use a resource bundle name inherited from its parent.
 466      *
 467      * @return localization bundle name (may be null)
 468      */
 469     public String getResourceBundleName() {
 470         return resourceBundleName;
 471     }
 472 
 473     /**
 474      * Set a filter to control output on this Logger.
 475      * <P>
 476      * After passing the initial "level" check, the Logger will
 477      * call this Filter to check if a log record should really
 478      * be published.
 479      *
 480      * @param   newFilter  a filter object (may be null)
 481      * @exception  SecurityException  if a security manager exists and if
 482      *             the caller does not have LoggingPermission("control").
 483      */
 484     public void setFilter(Filter newFilter) throws SecurityException {
 485         checkPermission();
 486         filter = newFilter;
 487     }
 488 
 489     /**
 490      * Get the current filter for this Logger.
 491      *
 492      * @return  a filter object (may be null)
 493      */
 494     public Filter getFilter() {
 495         return filter;
 496     }
 497 
 498     /**
 499      * Log a LogRecord.
 500      * <p>
 501      * All the other logging methods in this class call through
 502      * this method to actually perform any logging.  Subclasses can
 503      * override this single method to capture all log activity.
 504      *
 505      * @param record the LogRecord to be published
 506      */
 507     public void log(LogRecord record) {
 508         if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
 509             return;
 510         }
 511         Filter theFilter = filter;
 512         if (theFilter != null && !theFilter.isLoggable(record)) {
 513             return;
 514         }
 515 
 516         // Post the LogRecord to all our Handlers, and then to
 517         // our parents' handlers, all the way up the tree.
 518 
 519         Logger logger = this;
 520         while (logger != null) {
 521             for (Handler handler : logger.getHandlers()) {
 522                 handler.publish(record);
 523             }
 524 
 525             if (!logger.getUseParentHandlers()) {
 526                 break;
 527             }
 528 
 529             logger = logger.getParent();
 530         }
 531     }
 532 
 533     // private support method for logging.
 534     // We fill in the logger name, resource bundle name, and
 535     // resource bundle and then call "void log(LogRecord)".
 536     private void doLog(LogRecord lr) {
 537         lr.setLoggerName(name);
 538         String ebname = getEffectiveResourceBundleName();
 539         if (ebname != null) {
 540             lr.setResourceBundleName(ebname);
 541             lr.setResourceBundle(findResourceBundle(ebname));
 542         }
 543         log(lr);
 544     }
 545 
 546 
 547     //================================================================
 548     // Start of convenience methods WITHOUT className and methodName
 549     //================================================================
 550 
 551     /**
 552      * Log a message, with no arguments.
 553      * <p>
 554      * If the logger is currently enabled for the given message
 555      * level then the given message is forwarded to all the
 556      * registered output Handler objects.
 557      * <p>
 558      * @param   level   One of the message level identifiers, e.g., SEVERE
 559      * @param   msg     The string message (or a key in the message catalog)
 560      */
 561     public void log(Level level, String msg) {
 562         if (level.intValue() < levelValue || levelValue == offValue) {
 563             return;
 564         }
 565         LogRecord lr = new LogRecord(level, msg);
 566         doLog(lr);
 567     }
 568 
 569     /**
 570      * Log a message, with one object parameter.
 571      * <p>
 572      * If the logger is currently enabled for the given message
 573      * level then a corresponding LogRecord is created and forwarded
 574      * to all the registered output Handler objects.
 575      * <p>
 576      * @param   level   One of the message level identifiers, e.g., SEVERE
 577      * @param   msg     The string message (or a key in the message catalog)
 578      * @param   param1  parameter to the message
 579      */
 580     public void log(Level level, String msg, Object param1) {
 581         if (level.intValue() < levelValue || levelValue == offValue) {
 582             return;
 583         }
 584         LogRecord lr = new LogRecord(level, msg);
 585         Object params[] = { param1 };
 586         lr.setParameters(params);
 587         doLog(lr);
 588     }
 589 
 590     /**
 591      * Log a message, with an array of object arguments.
 592      * <p>
 593      * If the logger is currently enabled for the given message
 594      * level then a corresponding LogRecord is created and forwarded
 595      * to all the registered output Handler objects.
 596      * <p>
 597      * @param   level   One of the message level identifiers, e.g., SEVERE
 598      * @param   msg     The string message (or a key in the message catalog)
 599      * @param   params  array of parameters to the message
 600      */
 601     public void log(Level level, String msg, Object params[]) {
 602         if (level.intValue() < levelValue || levelValue == offValue) {
 603             return;
 604         }
 605         LogRecord lr = new LogRecord(level, msg);
 606         lr.setParameters(params);
 607         doLog(lr);
 608     }
 609 
 610     /**
 611      * Log a message, with associated Throwable information.
 612      * <p>
 613      * If the logger is currently enabled for the given message
 614      * level then the given arguments are stored in a LogRecord
 615      * which is forwarded to all registered output handlers.
 616      * <p>
 617      * Note that the thrown argument is stored in the LogRecord thrown
 618      * property, rather than the LogRecord parameters property.  Thus is it
 619      * processed specially by output Formatters and is not treated
 620      * as a formatting parameter to the LogRecord message property.
 621      * <p>
 622      * @param   level   One of the message level identifiers, e.g., SEVERE
 623      * @param   msg     The string message (or a key in the message catalog)
 624      * @param   thrown  Throwable associated with log message.
 625      */
 626     public void log(Level level, String msg, Throwable thrown) {
 627         if (level.intValue() < levelValue || levelValue == offValue) {
 628             return;
 629         }
 630         LogRecord lr = new LogRecord(level, msg);
 631         lr.setThrown(thrown);
 632         doLog(lr);
 633     }
 634 
 635     //================================================================
 636     // Start of convenience methods WITH className and methodName
 637     //================================================================
 638 
 639     /**
 640      * Log a message, specifying source class and method,
 641      * with no arguments.
 642      * <p>
 643      * If the logger is currently enabled for the given message
 644      * level then the given message is forwarded to all the
 645      * registered output Handler objects.
 646      * <p>
 647      * @param   level   One of the message level identifiers, e.g., SEVERE
 648      * @param   sourceClass    name of class that issued the logging request
 649      * @param   sourceMethod   name of method that issued the logging request
 650      * @param   msg     The string message (or a key in the message catalog)
 651      */
 652     public void logp(Level level, String sourceClass, String sourceMethod, String msg) {
 653         if (level.intValue() < levelValue || levelValue == offValue) {
 654             return;
 655         }
 656         LogRecord lr = new LogRecord(level, msg);
 657         lr.setSourceClassName(sourceClass);
 658         lr.setSourceMethodName(sourceMethod);
 659         doLog(lr);
 660     }
 661 
 662     /**
 663      * Log a message, specifying source class and method,
 664      * with a single object parameter to the log message.
 665      * <p>
 666      * If the logger is currently enabled for the given message
 667      * level then a corresponding LogRecord is created and forwarded
 668      * to all the registered output Handler objects.
 669      * <p>
 670      * @param   level   One of the message level identifiers, e.g., SEVERE
 671      * @param   sourceClass    name of class that issued the logging request
 672      * @param   sourceMethod   name of method that issued the logging request
 673      * @param   msg      The string message (or a key in the message catalog)
 674      * @param   param1    Parameter to the log message.
 675      */
 676     public void logp(Level level, String sourceClass, String sourceMethod,
 677                                                 String msg, Object param1) {
 678         if (level.intValue() < levelValue || levelValue == offValue) {
 679             return;
 680         }
 681         LogRecord lr = new LogRecord(level, msg);
 682         lr.setSourceClassName(sourceClass);
 683         lr.setSourceMethodName(sourceMethod);
 684         Object params[] = { param1 };
 685         lr.setParameters(params);
 686         doLog(lr);
 687     }
 688 
 689     /**
 690      * Log a message, specifying source class and method,
 691      * with an array of object arguments.
 692      * <p>
 693      * If the logger is currently enabled for the given message
 694      * level then a corresponding LogRecord is created and forwarded
 695      * to all the registered output Handler objects.
 696      * <p>
 697      * @param   level   One of the message level identifiers, e.g., SEVERE
 698      * @param   sourceClass    name of class that issued the logging request
 699      * @param   sourceMethod   name of method that issued the logging request
 700      * @param   msg     The string message (or a key in the message catalog)
 701      * @param   params  Array of parameters to the message
 702      */
 703     public void logp(Level level, String sourceClass, String sourceMethod,
 704                                                 String msg, Object params[]) {
 705         if (level.intValue() < levelValue || levelValue == offValue) {
 706             return;
 707         }
 708         LogRecord lr = new LogRecord(level, msg);
 709         lr.setSourceClassName(sourceClass);
 710         lr.setSourceMethodName(sourceMethod);
 711         lr.setParameters(params);
 712         doLog(lr);
 713     }
 714 
 715     /**
 716      * Log a message, specifying source class and method,
 717      * with associated Throwable information.
 718      * <p>
 719      * If the logger is currently enabled for the given message
 720      * level then the given arguments are stored in a LogRecord
 721      * which is forwarded to all registered output handlers.
 722      * <p>
 723      * Note that the thrown argument is stored in the LogRecord thrown
 724      * property, rather than the LogRecord parameters property.  Thus is it
 725      * processed specially by output Formatters and is not treated
 726      * as a formatting parameter to the LogRecord message property.
 727      * <p>
 728      * @param   level   One of the message level identifiers, e.g., SEVERE
 729      * @param   sourceClass    name of class that issued the logging request
 730      * @param   sourceMethod   name of method that issued the logging request
 731      * @param   msg     The string message (or a key in the message catalog)
 732      * @param   thrown  Throwable associated with log message.
 733      */
 734     public void logp(Level level, String sourceClass, String sourceMethod,
 735                                                         String msg, Throwable thrown) {
 736         if (level.intValue() < levelValue || levelValue == offValue) {
 737             return;
 738         }
 739         LogRecord lr = new LogRecord(level, msg);
 740         lr.setSourceClassName(sourceClass);
 741         lr.setSourceMethodName(sourceMethod);
 742         lr.setThrown(thrown);
 743         doLog(lr);
 744     }
 745 
 746 
 747     //=========================================================================
 748     // Start of convenience methods WITH className, methodName and bundle name.
 749     //=========================================================================
 750 
 751     // Private support method for logging for "logrb" methods.
 752     // We fill in the logger name, resource bundle name, and
 753     // resource bundle and then call "void log(LogRecord)".
 754     private void doLog(LogRecord lr, String rbname) {
 755         lr.setLoggerName(name);
 756         if (rbname != null) {
 757             lr.setResourceBundleName(rbname);
 758             lr.setResourceBundle(findResourceBundle(rbname));
 759         }
 760         log(lr);
 761     }
 762 
 763     /**
 764      * Log a message, specifying source class, method, and resource bundle name
 765      * with no arguments.
 766      * <p>
 767      * If the logger is currently enabled for the given message
 768      * level then the given message is forwarded to all the
 769      * registered output Handler objects.
 770      * <p>
 771      * The msg string is localized using the named resource bundle.  If the
 772      * resource bundle name is null, or an empty String or invalid
 773      * then the msg string is not localized.
 774      * <p>
 775      * @param   level   One of the message level identifiers, e.g., SEVERE
 776      * @param   sourceClass    name of class that issued the logging request
 777      * @param   sourceMethod   name of method that issued the logging request
 778      * @param   bundleName     name of resource bundle to localize msg,
 779      *                         can be null
 780      * @param   msg     The string message (or a key in the message catalog)
 781      */
 782 
 783     public void logrb(Level level, String sourceClass, String sourceMethod,
 784                                 String bundleName, String msg) {
 785         if (level.intValue() < levelValue || levelValue == offValue) {
 786             return;
 787         }
 788         LogRecord lr = new LogRecord(level, msg);
 789         lr.setSourceClassName(sourceClass);
 790         lr.setSourceMethodName(sourceMethod);
 791         doLog(lr, bundleName);
 792     }
 793 
 794     /**
 795      * Log a message, specifying source class, method, and resource bundle name,
 796      * with a single object parameter to the log message.
 797      * <p>
 798      * If the logger is currently enabled for the given message
 799      * level then a corresponding LogRecord is created and forwarded
 800      * to all the registered output Handler objects.
 801      * <p>
 802      * The msg string is localized using the named resource bundle.  If the
 803      * resource bundle name is null, or an empty String or invalid
 804      * then the msg string is not localized.
 805      * <p>
 806      * @param   level   One of the message level identifiers, e.g., SEVERE
 807      * @param   sourceClass    name of class that issued the logging request
 808      * @param   sourceMethod   name of method that issued the logging request
 809      * @param   bundleName     name of resource bundle to localize msg,
 810      *                         can be null
 811      * @param   msg      The string message (or a key in the message catalog)
 812      * @param   param1    Parameter to the log message.
 813      */
 814     public void logrb(Level level, String sourceClass, String sourceMethod,
 815                                 String bundleName, String msg, Object param1) {
 816         if (level.intValue() < levelValue || levelValue == offValue) {
 817             return;
 818         }
 819         LogRecord lr = new LogRecord(level, msg);
 820         lr.setSourceClassName(sourceClass);
 821         lr.setSourceMethodName(sourceMethod);
 822         Object params[] = { param1 };
 823         lr.setParameters(params);
 824         doLog(lr, bundleName);
 825     }
 826 
 827     /**
 828      * Log a message, specifying source class, method, and resource bundle name,
 829      * with an array of object arguments.
 830      * <p>
 831      * If the logger is currently enabled for the given message
 832      * level then a corresponding LogRecord is created and forwarded
 833      * to all the registered output Handler objects.
 834      * <p>
 835      * The msg string is localized using the named resource bundle.  If the
 836      * resource bundle name is null, or an empty String or invalid
 837      * then the msg string is not localized.
 838      * <p>
 839      * @param   level   One of the message level identifiers, e.g., SEVERE
 840      * @param   sourceClass    name of class that issued the logging request
 841      * @param   sourceMethod   name of method that issued the logging request
 842      * @param   bundleName     name of resource bundle to localize msg,
 843      *                         can be null.
 844      * @param   msg     The string message (or a key in the message catalog)
 845      * @param   params  Array of parameters to the message
 846      */
 847     public void logrb(Level level, String sourceClass, String sourceMethod,
 848                                 String bundleName, String msg, Object params[]) {
 849         if (level.intValue() < levelValue || levelValue == offValue) {
 850             return;
 851         }
 852         LogRecord lr = new LogRecord(level, msg);
 853         lr.setSourceClassName(sourceClass);
 854         lr.setSourceMethodName(sourceMethod);
 855         lr.setParameters(params);
 856         doLog(lr, bundleName);
 857     }
 858 
 859     /**
 860      * Log a message, specifying source class, method, and resource bundle name,
 861      * with associated Throwable information.
 862      * <p>
 863      * If the logger is currently enabled for the given message
 864      * level then the given arguments are stored in a LogRecord
 865      * which is forwarded to all registered output handlers.
 866      * <p>
 867      * The msg string is localized using the named resource bundle.  If the
 868      * resource bundle name is null, or an empty String or invalid
 869      * then the msg string is not localized.
 870      * <p>
 871      * Note that the thrown argument is stored in the LogRecord thrown
 872      * property, rather than the LogRecord parameters property.  Thus is it
 873      * processed specially by output Formatters and is not treated
 874      * as a formatting parameter to the LogRecord message property.
 875      * <p>
 876      * @param   level   One of the message level identifiers, e.g., SEVERE
 877      * @param   sourceClass    name of class that issued the logging request
 878      * @param   sourceMethod   name of method that issued the logging request
 879      * @param   bundleName     name of resource bundle to localize msg,
 880      *                         can be null
 881      * @param   msg     The string message (or a key in the message catalog)
 882      * @param   thrown  Throwable associated with log message.
 883      */
 884     public void logrb(Level level, String sourceClass, String sourceMethod,
 885                                         String bundleName, String msg, Throwable thrown) {
 886         if (level.intValue() < levelValue || levelValue == offValue) {
 887             return;
 888         }
 889         LogRecord lr = new LogRecord(level, msg);
 890         lr.setSourceClassName(sourceClass);
 891         lr.setSourceMethodName(sourceMethod);
 892         lr.setThrown(thrown);
 893         doLog(lr, bundleName);
 894     }
 895 
 896 
 897     //======================================================================
 898     // Start of convenience methods for logging method entries and returns.
 899     //======================================================================
 900 
 901     /**
 902      * Log a method entry.
 903      * <p>
 904      * This is a convenience method that can be used to log entry
 905      * to a method.  A LogRecord with message "ENTRY", log level
 906      * FINER, and the given sourceMethod and sourceClass is logged.
 907      * <p>
 908      * @param   sourceClass    name of class that issued the logging request
 909      * @param   sourceMethod   name of method that is being entered
 910      */
 911     public void entering(String sourceClass, String sourceMethod) {
 912         if (Level.FINER.intValue() < levelValue) {
 913             return;
 914         }
 915         logp(Level.FINER, sourceClass, sourceMethod, "ENTRY");
 916     }
 917 
 918     /**
 919      * Log a method entry, with one parameter.
 920      * <p>
 921      * This is a convenience method that can be used to log entry
 922      * to a method.  A LogRecord with message "ENTRY {0}", log level
 923      * FINER, and the given sourceMethod, sourceClass, and parameter
 924      * is logged.
 925      * <p>
 926      * @param   sourceClass    name of class that issued the logging request
 927      * @param   sourceMethod   name of method that is being entered
 928      * @param   param1         parameter to the method being entered
 929      */
 930     public void entering(String sourceClass, String sourceMethod, Object param1) {
 931         if (Level.FINER.intValue() < levelValue) {
 932             return;
 933         }
 934         Object params[] = { param1 };
 935         logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", params);
 936     }
 937 
 938     /**
 939      * Log a method entry, with an array of parameters.
 940      * <p>
 941      * This is a convenience method that can be used to log entry
 942      * to a method.  A LogRecord with message "ENTRY" (followed by a
 943      * format {N} indicator for each entry in the parameter array),
 944      * log level FINER, and the given sourceMethod, sourceClass, and
 945      * parameters is logged.
 946      * <p>
 947      * @param   sourceClass    name of class that issued the logging request
 948      * @param   sourceMethod   name of method that is being entered
 949      * @param   params         array of parameters to the method being entered
 950      */
 951     public void entering(String sourceClass, String sourceMethod, Object params[]) {
 952         if (Level.FINER.intValue() < levelValue) {
 953             return;
 954         }
 955         String msg = "ENTRY";
 956         if (params == null ) {
 957            logp(Level.FINER, sourceClass, sourceMethod, msg);
 958            return;
 959         }
 960         for (int i = 0; i < params.length; i++) {
 961             msg = msg + " {" + i + "}";
 962         }
 963         logp(Level.FINER, sourceClass, sourceMethod, msg, params);
 964     }
 965 
 966     /**
 967      * Log a method return.
 968      * <p>
 969      * This is a convenience method that can be used to log returning
 970      * from a method.  A LogRecord with message "RETURN", log level
 971      * FINER, and the given sourceMethod and sourceClass is logged.
 972      * <p>
 973      * @param   sourceClass    name of class that issued the logging request
 974      * @param   sourceMethod   name of the method
 975      */
 976     public void exiting(String sourceClass, String sourceMethod) {
 977         if (Level.FINER.intValue() < levelValue) {
 978             return;
 979         }
 980         logp(Level.FINER, sourceClass, sourceMethod, "RETURN");
 981     }
 982 
 983 
 984     /**
 985      * Log a method return, with result object.
 986      * <p>
 987      * This is a convenience method that can be used to log returning
 988      * from a method.  A LogRecord with message "RETURN {0}", log level
 989      * FINER, and the gives sourceMethod, sourceClass, and result
 990      * object is logged.
 991      * <p>
 992      * @param   sourceClass    name of class that issued the logging request
 993      * @param   sourceMethod   name of the method
 994      * @param   result  Object that is being returned
 995      */
 996     public void exiting(String sourceClass, String sourceMethod, Object result) {
 997         if (Level.FINER.intValue() < levelValue) {
 998             return;
 999         }
1000         Object params[] = { result };
1001         logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result);
1002     }
1003 
1004     /**
1005      * Log throwing an exception.
1006      * <p>
1007      * This is a convenience method to log that a method is
1008      * terminating by throwing an exception.  The logging is done
1009      * using the FINER level.
1010      * <p>
1011      * If the logger is currently enabled for the given message
1012      * level then the given arguments are stored in a LogRecord
1013      * which is forwarded to all registered output handlers.  The
1014      * LogRecord's message is set to "THROW".
1015      * <p>
1016      * Note that the thrown argument is stored in the LogRecord thrown
1017      * property, rather than the LogRecord parameters property.  Thus is it
1018      * processed specially by output Formatters and is not treated
1019      * as a formatting parameter to the LogRecord message property.
1020      * <p>
1021      * @param   sourceClass    name of class that issued the logging request
1022      * @param   sourceMethod  name of the method.
1023      * @param   thrown  The Throwable that is being thrown.
1024      */
1025     public void throwing(String sourceClass, String sourceMethod, Throwable thrown) {
1026         if (Level.FINER.intValue() < levelValue || levelValue == offValue ) {
1027             return;
1028         }
1029         LogRecord lr = new LogRecord(Level.FINER, "THROW");
1030         lr.setSourceClassName(sourceClass);
1031         lr.setSourceMethodName(sourceMethod);
1032         lr.setThrown(thrown);
1033         doLog(lr);
1034     }
1035 
1036     //=======================================================================
1037     // Start of simple convenience methods using level names as method names
1038     //=======================================================================
1039 
1040     /**
1041      * Log a SEVERE message.
1042      * <p>
1043      * If the logger is currently enabled for the SEVERE message
1044      * level then the given message is forwarded to all the
1045      * registered output Handler objects.
1046      * <p>
1047      * @param   msg     The string message (or a key in the message catalog)
1048      */
1049     public void severe(String msg) {
1050         if (Level.SEVERE.intValue() < levelValue) {
1051             return;
1052         }
1053         log(Level.SEVERE, msg);
1054     }
1055 
1056     /**
1057      * Log a WARNING message.
1058      * <p>
1059      * If the logger is currently enabled for the WARNING message
1060      * level then the given message is forwarded to all the
1061      * registered output Handler objects.
1062      * <p>
1063      * @param   msg     The string message (or a key in the message catalog)
1064      */
1065     public void warning(String msg) {
1066         if (Level.WARNING.intValue() < levelValue) {
1067             return;
1068         }
1069         log(Level.WARNING, msg);
1070     }
1071 
1072     /**
1073      * Log an INFO message.
1074      * <p>
1075      * If the logger is currently enabled for the INFO message
1076      * level then the given message is forwarded to all the
1077      * registered output Handler objects.
1078      * <p>
1079      * @param   msg     The string message (or a key in the message catalog)
1080      */
1081     public void info(String msg) {
1082         if (Level.INFO.intValue() < levelValue) {
1083             return;
1084         }
1085         log(Level.INFO, msg);
1086     }
1087 
1088     /**
1089      * Log a CONFIG message.
1090      * <p>
1091      * If the logger is currently enabled for the CONFIG message
1092      * level then the given message is forwarded to all the
1093      * registered output Handler objects.
1094      * <p>
1095      * @param   msg     The string message (or a key in the message catalog)
1096      */
1097     public void config(String msg) {
1098         if (Level.CONFIG.intValue() < levelValue) {
1099             return;
1100         }
1101         log(Level.CONFIG, msg);
1102     }
1103 
1104     /**
1105      * Log a FINE message.
1106      * <p>
1107      * If the logger is currently enabled for the FINE message
1108      * level then the given message is forwarded to all the
1109      * registered output Handler objects.
1110      * <p>
1111      * @param   msg     The string message (or a key in the message catalog)
1112      */
1113     public void fine(String msg) {
1114         if (Level.FINE.intValue() < levelValue) {
1115             return;
1116         }
1117         log(Level.FINE, msg);
1118     }
1119 
1120     /**
1121      * Log a FINER message.
1122      * <p>
1123      * If the logger is currently enabled for the FINER message
1124      * level then the given message is forwarded to all the
1125      * registered output Handler objects.
1126      * <p>
1127      * @param   msg     The string message (or a key in the message catalog)
1128      */
1129     public void finer(String msg) {
1130         if (Level.FINER.intValue() < levelValue) {
1131             return;
1132         }
1133         log(Level.FINER, msg);
1134     }
1135 
1136     /**
1137      * Log a FINEST message.
1138      * <p>
1139      * If the logger is currently enabled for the FINEST message
1140      * level then the given message is forwarded to all the
1141      * registered output Handler objects.
1142      * <p>
1143      * @param   msg     The string message (or a key in the message catalog)
1144      */
1145     public void finest(String msg) {
1146         if (Level.FINEST.intValue() < levelValue) {
1147             return;
1148         }
1149         log(Level.FINEST, msg);
1150     }
1151 
1152     //================================================================
1153     // End of convenience methods
1154     //================================================================
1155 
1156     /**
1157      * Set the log level specifying which message levels will be
1158      * logged by this logger.  Message levels lower than this
1159      * value will be discarded.  The level value Level.OFF
1160      * can be used to turn off logging.
1161      * <p>
1162      * If the new level is null, it means that this node should
1163      * inherit its level from its nearest ancestor with a specific
1164      * (non-null) level value.
1165      *
1166      * @param newLevel   the new value for the log level (may be null)
1167      * @exception  SecurityException  if a security manager exists and if
1168      *             the caller does not have LoggingPermission("control").
1169      */
1170     public void setLevel(Level newLevel) throws SecurityException {
1171         checkPermission();
1172         synchronized (treeLock) {
1173             levelObject = newLevel;
1174             updateEffectiveLevel();
1175         }
1176     }
1177 
1178     /**
1179      * Get the log Level that has been specified for this Logger.
1180      * The result may be null, which means that this logger's
1181      * effective level will be inherited from its parent.
1182      *
1183      * @return  this Logger's level
1184      */
1185     public Level getLevel() {
1186         return levelObject;
1187     }
1188 
1189     /**
1190      * Check if a message of the given level would actually be logged
1191      * by this logger.  This check is based on the Loggers effective level,
1192      * which may be inherited from its parent.
1193      *
1194      * @param   level   a message logging level
1195      * @return  true if the given message level is currently being logged.
1196      */
1197     public boolean isLoggable(Level level) {
1198         if (level.intValue() < levelValue || levelValue == offValue) {
1199             return false;
1200         }
1201         return true;
1202     }
1203 
1204     /**
1205      * Get the name for this logger.
1206      * @return logger name.  Will be null for anonymous Loggers.
1207      */
1208     public String getName() {
1209         return name;
1210     }
1211 
1212     /**
1213      * Add a log Handler to receive logging messages.
1214      * <p>
1215      * By default, Loggers also send their output to their parent logger.
1216      * Typically the root Logger is configured with a set of Handlers
1217      * that essentially act as default handlers for all loggers.
1218      *
1219      * @param   handler a logging Handler
1220      * @exception  SecurityException  if a security manager exists and if
1221      *             the caller does not have LoggingPermission("control").
1222      */
1223     public void addHandler(Handler handler) throws SecurityException {
1224         // Check for null handler
1225         handler.getClass();
1226         checkPermission();
1227         handlers.add(handler);
1228     }
1229 
1230     /**
1231      * Remove a log Handler.
1232      * <P>
1233      * Returns silently if the given Handler is not found or is null
1234      *
1235      * @param   handler a logging Handler
1236      * @exception  SecurityException  if a security manager exists and if
1237      *             the caller does not have LoggingPermission("control").
1238      */
1239     public void removeHandler(Handler handler) throws SecurityException {
1240         checkPermission();
1241         if (handler == null) {
1242             return;
1243         }
1244         handlers.remove(handler);
1245     }
1246 
1247     /**
1248      * Get the Handlers associated with this logger.
1249      * <p>
1250      * @return  an array of all registered Handlers
1251      */
1252     public Handler[] getHandlers() {
1253         return handlers.toArray(emptyHandlers);
1254     }
1255 
1256     /**
1257      * Specify whether or not this logger should send its output
1258      * to its parent Logger.  This means that any LogRecords will
1259      * also be written to the parent's Handlers, and potentially
1260      * to its parent, recursively up the namespace.
1261      *
1262      * @param useParentHandlers   true if output is to be sent to the
1263      *          logger's parent.
1264      * @exception  SecurityException  if a security manager exists and if
1265      *             the caller does not have LoggingPermission("control").
1266      */
1267     public void setUseParentHandlers(boolean useParentHandlers) {
1268         checkPermission();
1269         this.useParentHandlers = useParentHandlers;
1270     }
1271 
1272     /**
1273      * Discover whether or not this logger is sending its output
1274      * to its parent logger.
1275      *
1276      * @return  true if output is to be sent to the logger's parent
1277      */
1278     public boolean getUseParentHandlers() {
1279         return useParentHandlers;
1280     }
1281 
1282     // Private utility method to map a resource bundle name to an
1283     // actual resource bundle, using a simple one-entry cache.
1284     // Returns null for a null name.
1285     // May also return null if we can't find the resource bundle and
1286     // there is no suitable previous cached value.
1287 
1288     private synchronized ResourceBundle findResourceBundle(String name) {
1289         // Return a null bundle for a null name.
1290         if (name == null) {
1291             return null;
1292         }
1293 
1294         Locale currentLocale = Locale.getDefault();
1295 
1296         // Normally we should hit on our simple one entry cache.
1297         if (catalog != null && currentLocale == catalogLocale
1298                                         && name == catalogName) {
1299             return catalog;
1300         }
1301 
1302         // Use the thread's context ClassLoader.  If there isn't one,
1303         // use the SystemClassloader.
1304         ClassLoader cl = Thread.currentThread().getContextClassLoader();
1305         if (cl == null) {
1306             cl = ClassLoader.getSystemClassLoader();
1307         }
1308         try {
1309             catalog = ResourceBundle.getBundle(name, currentLocale, cl);
1310             catalogName = name;
1311             catalogLocale = currentLocale;
1312             return catalog;
1313         } catch (MissingResourceException ex) {
1314             // Woops.  We can't find the ResourceBundle in the default
1315             // ClassLoader.  Drop through.
1316         }
1317 
1318 
1319         // Fall back to searching up the call stack and trying each
1320         // calling ClassLoader.
1321         for (int ix = 0; ; ix++) {
1322             Class clz = sun.reflect.Reflection.getCallerClass(ix);
1323             if (clz == null) {
1324                 break;
1325             }
1326             ClassLoader cl2 = clz.getClassLoader();
1327             if (cl2 == null) {
1328                 cl2 = ClassLoader.getSystemClassLoader();
1329             }
1330             if (cl == cl2) {
1331                 // We've already checked this classloader.
1332                 continue;
1333             }
1334             cl = cl2;
1335             try {
1336                 catalog = ResourceBundle.getBundle(name, currentLocale, cl);
1337                 catalogName = name;
1338                 catalogLocale = currentLocale;
1339                 return catalog;
1340             } catch (MissingResourceException ex) {
1341                 // Ok, this one didn't work either.
1342                 // Drop through, and try the next one.
1343             }
1344         }
1345 
1346         if (name.equals(catalogName)) {
1347             // Return the previous cached value for that name.
1348             // This may be null.
1349             return catalog;
1350         }
1351         // Sorry, we're out of luck.
1352         return null;
1353     }
1354 
1355     // Private utility method to initialize our one entry
1356     // resource bundle name cache.
1357     // Note: for consistency reasons, we are careful to check
1358     // that a suitable ResourceBundle exists before setting the
1359     // resourceBundleName field.
1360     // Synchronized to prevent races in setting the field.
1361     private synchronized void setupResourceInfo(String name) {
1362         if (name == null) {
1363             return;
1364         }
1365 
1366         if (resourceBundleName != null) {
1367             // this Logger already has a ResourceBundle
1368 
1369             if (resourceBundleName.equals(name)) {
1370                 // the names match so there is nothing more to do
1371                 return;
1372             }
1373 
1374             // cannot change ResourceBundles once they are set
1375             throw new IllegalArgumentException(
1376                 resourceBundleName + " != " + name);
1377         }
1378 
1379         ResourceBundle rb = findResourceBundle(name);
1380         if (rb == null) {
1381             // We've failed to find an expected ResourceBundle.
1382             throw new MissingResourceException("Can't find " + name + " bundle", name, "");
1383         }
1384         resourceBundleName = name;
1385     }
1386 
1387     /**
1388      * Return the parent for this Logger.
1389      * <p>
1390      * This method returns the nearest extant parent in the namespace.
1391      * Thus if a Logger is called "a.b.c.d", and a Logger called "a.b"
1392      * has been created but no logger "a.b.c" exists, then a call of
1393      * getParent on the Logger "a.b.c.d" will return the Logger "a.b".
1394      * <p>
1395      * The result will be null if it is called on the root Logger
1396      * in the namespace.
1397      *
1398      * @return nearest existing parent Logger
1399      */
1400     public Logger getParent() {
1401         // Note: this used to be synchronized on treeLock.  However, this only
1402         // provided memory semantics, as there was no guarantee that the caller
1403         // would synchronize on treeLock (in fact, there is no way for external
1404         // callers to so synchronize).  Therefore, we have made parent volatile
1405         // instead.
1406         return parent;
1407     }
1408 
1409     /**
1410      * Set the parent for this Logger.  This method is used by
1411      * the LogManager to update a Logger when the namespace changes.
1412      * <p>
1413      * It should not be called from application code.
1414      * <p>
1415      * @param  parent   the new parent logger
1416      * @exception  SecurityException  if a security manager exists and if
1417      *             the caller does not have LoggingPermission("control").
1418      */
1419     public void setParent(Logger parent) {
1420         if (parent == null) {
1421             throw new NullPointerException();
1422         }
1423         manager.checkPermission();
1424         doSetParent(parent);
1425     }
1426 
1427     // Private method to do the work for parenting a child
1428     // Logger onto a parent logger.
1429     private void doSetParent(Logger newParent) {
1430 
1431         // System.err.println("doSetParent \"" + getName() + "\" \""
1432         //                              + newParent.getName() + "\"");
1433 
1434         synchronized (treeLock) {
1435 
1436             // Remove ourself from any previous parent.
1437             LogManager.LoggerWeakRef ref = null;
1438             if (parent != null) {
1439                 // assert parent.kids != null;
1440                 for (Iterator<LogManager.LoggerWeakRef> iter = parent.kids.iterator(); iter.hasNext(); ) {
1441                     ref = iter.next();
1442                     Logger kid =  ref.get();
1443                     if (kid == this) {
1444                         // ref is used down below to complete the reparenting
1445                         iter.remove();
1446                         break;
1447                     } else {
1448                         ref = null;
1449                     }
1450                 }
1451                 // We have now removed ourself from our parents' kids.
1452             }
1453 
1454             // Set our new parent.
1455             parent = newParent;
1456             if (parent.kids == null) {
1457                 parent.kids = new ArrayList<>(2);
1458             }
1459             if (ref == null) {
1460                 // we didn't have a previous parent
1461                 ref = manager.new LoggerWeakRef(this);
1462             }
1463             ref.setParentRef(new WeakReference<Logger>(parent));
1464             parent.kids.add(ref);
1465 
1466             // As a result of the reparenting, the effective level
1467             // may have changed for us and our children.
1468             updateEffectiveLevel();
1469 
1470         }
1471     }
1472 
1473     // Package-level method.
1474     // Remove the weak reference for the specified child Logger from the
1475     // kid list. We should only be called from LoggerWeakRef.dispose().
1476     final void removeChildLogger(LogManager.LoggerWeakRef child) {
1477         synchronized (treeLock) {
1478             for (Iterator<LogManager.LoggerWeakRef> iter = kids.iterator(); iter.hasNext(); ) {
1479                 LogManager.LoggerWeakRef ref = iter.next();
1480                 if (ref == child) {
1481                     iter.remove();
1482                     return;
1483                 }
1484             }
1485         }
1486     }
1487 
1488     // Recalculate the effective level for this node and
1489     // recursively for our children.
1490 
1491     private void updateEffectiveLevel() {
1492         // assert Thread.holdsLock(treeLock);
1493 
1494         // Figure out our current effective level.
1495         int newLevelValue;
1496         if (levelObject != null) {
1497             newLevelValue = levelObject.intValue();
1498         } else {
1499             if (parent != null) {
1500                 newLevelValue = parent.levelValue;
1501             } else {
1502                 // This may happen during initialization.
1503                 newLevelValue = Level.INFO.intValue();
1504             }
1505         }
1506 
1507         // If our effective value hasn't changed, we're done.
1508         if (levelValue == newLevelValue) {
1509             return;
1510         }
1511 
1512         levelValue = newLevelValue;
1513 
1514         // System.err.println("effective level: \"" + getName() + "\" := " + level);
1515 
1516         // Recursively update the level on each of our kids.
1517         if (kids != null) {
1518             for (int i = 0; i < kids.size(); i++) {
1519                 LogManager.LoggerWeakRef ref = kids.get(i);
1520                 Logger kid =  ref.get();
1521                 if (kid != null) {
1522                     kid.updateEffectiveLevel();
1523                 }
1524             }
1525         }
1526     }
1527 
1528 
1529     // Private method to get the potentially inherited
1530     // resource bundle name for this Logger.
1531     // May return null
1532     private String getEffectiveResourceBundleName() {
1533         Logger target = this;
1534         while (target != null) {
1535             String rbn = target.getResourceBundleName();
1536             if (rbn != null) {
1537                 return rbn;
1538             }
1539             target = target.getParent();
1540         }
1541         return null;
1542     }
1543 
1544 
1545 }