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