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