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