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