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.io.*;
  30 import java.util.*;
  31 import java.security.*;
  32 import java.lang.ref.ReferenceQueue;
  33 import java.lang.ref.WeakReference;
  34 import java.beans.PropertyChangeListener;
  35 import java.beans.PropertyChangeSupport;
  36 import java.net.URL;
  37 import sun.misc.JavaAWTAccess;
  38 import sun.misc.SharedSecrets;
  39 import sun.security.action.GetPropertyAction;
  40 
  41 /**
  42  * There is a single global LogManager object that is used to
  43  * maintain a set of shared state about Loggers and log services.
  44  * <p>
  45  * This LogManager object:
  46  * <ul>
  47  * <li> Manages a hierarchical namespace of Logger objects.  All
  48  *      named Loggers are stored in this namespace.
  49  * <li> Manages a set of logging control properties.  These are
  50  *      simple key-value pairs that can be used by Handlers and
  51  *      other logging objects to configure themselves.
  52  * </ul>
  53  * <p>
  54  * The global LogManager object can be retrieved using LogManager.getLogManager().
  55  * The LogManager object is created during class initialization and
  56  * cannot subsequently be changed.
  57  * <p>
  58  * At startup the LogManager class is located using the
  59  * java.util.logging.manager system property.
  60  * <p>
  61  * By default, the LogManager reads its initial configuration from
  62  * a properties file "lib/logging.properties" in the JRE directory.
  63  * If you edit that property file you can change the default logging
  64  * configuration for all uses of that JRE.
  65  * <p>
  66  * In addition, the LogManager uses two optional system properties that
  67  * allow more control over reading the initial configuration:
  68  * <ul>
  69  * <li>"java.util.logging.config.class"
  70  * <li>"java.util.logging.config.file"
  71  * </ul>
  72  * These two properties may be set via the Preferences API, or as
  73  * command line property definitions to the "java" command, or as
  74  * system property definitions passed to JNI_CreateJavaVM.
  75  * <p>
  76  * If the "java.util.logging.config.class" property is set, then the
  77  * property value is treated as a class name.  The given class will be
  78  * loaded, an object will be instantiated, and that object's constructor
  79  * is responsible for reading in the initial configuration.  (That object
  80  * may use other system properties to control its configuration.)  The
  81  * alternate configuration class can use <tt>readConfiguration(InputStream)</tt>
  82  * to define properties in the LogManager.
  83  * <p>
  84  * If "java.util.logging.config.class" property is <b>not</b> set,
  85  * then the "java.util.logging.config.file" system property can be used
  86  * to specify a properties file (in java.util.Properties format). The
  87  * initial logging configuration will be read from this file.
  88  * <p>
  89  * If neither of these properties is defined then, as described
  90  * above, the LogManager will read its initial configuration from
  91  * a properties file "lib/logging.properties" in the JRE directory.
  92  * <p>
  93  * The properties for loggers and Handlers will have names starting
  94  * with the dot-separated name for the handler or logger.
  95  * <p>
  96  * The global logging properties may include:
  97  * <ul>
  98  * <li>A property "handlers".  This defines a whitespace or comma separated
  99  * list of class names for handler classes to load and register as
 100  * handlers on the root Logger (the Logger named "").  Each class
 101  * name must be for a Handler class which has a default constructor.
 102  * Note that these Handlers may be created lazily, when they are
 103  * first used.
 104  *
 105  * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
 106  * comma separated list of class names for handlers classes to
 107  * load and register as handlers to the specified logger. Each class
 108  * name must be for a Handler class which has a default constructor.
 109  * Note that these Handlers may be created lazily, when they are
 110  * first used.
 111  *
 112  * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
 113  * value. By default every logger calls its parent in addition to
 114  * handling the logging message itself, this often result in messages
 115  * being handled by the root logger as well. When setting this property
 116  * to false a Handler needs to be configured for this logger otherwise
 117  * no logging messages are delivered.
 118  *
 119  * <li>A property "config".  This property is intended to allow
 120  * arbitrary configuration code to be run.  The property defines a
 121  * whitespace or comma separated list of class names.  A new instance will be
 122  * created for each named class.  The default constructor of each class
 123  * may execute arbitrary code to update the logging configuration, such as
 124  * setting logger levels, adding handlers, adding filters, etc.
 125  * </ul>
 126  * <p>
 127  * Note that all classes loaded during LogManager configuration are
 128  * first searched on the system class path before any user class path.
 129  * That includes the LogManager class, any config classes, and any
 130  * handler classes.
 131  * <p>
 132  * Loggers are organized into a naming hierarchy based on their
 133  * dot separated names.  Thus "a.b.c" is a child of "a.b", but
 134  * "a.b1" and a.b2" are peers.
 135  * <p>
 136  * All properties whose names end with ".level" are assumed to define
 137  * log levels for Loggers.  Thus "foo.level" defines a log level for
 138  * the logger called "foo" and (recursively) for any of its children
 139  * in the naming hierarchy.  Log Levels are applied in the order they
 140  * are defined in the properties file.  Thus level settings for child
 141  * nodes in the tree should come after settings for their parents.
 142  * The property name ".level" can be used to set the level for the
 143  * root of the tree.
 144  * <p>
 145  * All methods on the LogManager object are multi-thread safe.
 146  *
 147  * @since 1.4
 148 */
 149 
 150 public class LogManager {
 151     // The global LogManager object
 152     private static LogManager manager;
 153 
 154     private final static Handler[] emptyHandlers = { };
 155     private Properties props = new Properties();
 156     private PropertyChangeSupport changes
 157                          = new PropertyChangeSupport(LogManager.class);
 158     private final static Level defaultLevel = Level.INFO;
 159 
 160     // LoggerContext for system loggers and user loggers
 161     private final LoggerContext systemContext = new SystemLoggerContext();
 162     private final LoggerContext userContext = new LoggerContext();
 163     private Logger rootLogger;
 164 
 165     // Have we done the primordial reading of the configuration file?
 166     // (Must be done after a suitable amount of java.lang.System
 167     // initialization has been done)
 168     private volatile boolean readPrimordialConfiguration;
 169     // Have we initialized global (root) handlers yet?
 170     // This gets set to false in readConfiguration
 171     private boolean initializedGlobalHandlers = true;
 172     // True if JVM death is imminent and the exit hook has been called.
 173     private boolean deathImminent;
 174 
 175     static {
 176         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 177                 public Object run() {
 178                     String cname = null;
 179                     try {
 180                         cname = System.getProperty("java.util.logging.manager");
 181                         if (cname != null) {
 182                             try {
 183                                 Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 184                                 manager = (LogManager) clz.newInstance();
 185                             } catch (ClassNotFoundException ex) {
 186                                 Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 187                                 manager = (LogManager) clz.newInstance();
 188                             }
 189                         }
 190                     } catch (Exception ex) {
 191                         System.err.println("Could not load Logmanager \"" + cname + "\"");
 192                         ex.printStackTrace();
 193                     }
 194                     if (manager == null) {
 195                         manager = new LogManager();
 196                     }
 197 
 198                     // Create and retain Logger for the root of the namespace.
 199                     manager.rootLogger = manager.new RootLogger();
 200                     manager.addLogger(manager.rootLogger);
 201                     manager.systemContext.addLocalLogger(manager.rootLogger);
 202 
 203                     // Adding the global Logger. Doing so in the Logger.<clinit>
 204                     // would deadlock with the LogManager.<clinit>.
 205                     Logger.global.setLogManager(manager);
 206                     manager.addLogger(Logger.global);
 207 
 208                     // We don't call readConfiguration() here, as we may be running
 209                     // very early in the JVM startup sequence.  Instead readConfiguration
 210                     // will be called lazily in getLogManager().
 211                     return null;
 212                 }
 213             });
 214     }
 215 
 216 
 217     // This private class is used as a shutdown hook.
 218     // It does a "reset" to close all open handlers.
 219     private class Cleaner extends Thread {
 220 
 221         private Cleaner() {
 222             /* Set context class loader to null in order to avoid
 223              * keeping a strong reference to an application classloader.
 224              */
 225             this.setContextClassLoader(null);
 226         }
 227 
 228         public void run() {
 229             // This is to ensure the LogManager.<clinit> is completed
 230             // before synchronized block. Otherwise deadlocks are possible.
 231             LogManager mgr = manager;
 232 
 233             // If the global handlers haven't been initialized yet, we
 234             // don't want to initialize them just so we can close them!
 235             synchronized (LogManager.this) {
 236                 // Note that death is imminent.
 237                 deathImminent = true;
 238                 initializedGlobalHandlers = true;
 239             }
 240 
 241             // Do a reset to close all active handlers.
 242             reset();
 243         }
 244     }
 245 
 246 
 247     /**
 248      * Protected constructor.  This is protected so that container applications
 249      * (such as J2EE containers) can subclass the object.  It is non-public as
 250      * it is intended that there only be one LogManager object, whose value is
 251      * retrieved by calling Logmanager.getLogManager.
 252      */
 253     protected LogManager() {
 254         // Add a shutdown hook to close the global handlers.
 255         try {
 256             Runtime.getRuntime().addShutdownHook(new Cleaner());
 257         } catch (IllegalStateException e) {
 258             // If the VM is already shutting down,
 259             // We do not need to register shutdownHook.
 260         }
 261     }
 262 
 263     /**
 264      * Return the global LogManager object.
 265      */
 266     public static LogManager getLogManager() {
 267         if (manager != null) {
 268             manager.readPrimordialConfiguration();
 269         }
 270         return manager;
 271     }
 272 
 273     private void readPrimordialConfiguration() {
 274         if (!readPrimordialConfiguration) {
 275             synchronized (this) {
 276                 if (!readPrimordialConfiguration) {
 277                     // If System.in/out/err are null, it's a good
 278                     // indication that we're still in the
 279                     // bootstrapping phase
 280                     if (System.out == null) {
 281                         return;
 282                     }
 283                     readPrimordialConfiguration = true;
 284 
 285                     try {
 286                         AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
 287                                 public Void run() throws Exception {
 288                                     readConfiguration();
 289 
 290                                     // Platform loggers begin to delegate to java.util.logging.Logger
 291                                     sun.util.logging.PlatformLogger.redirectPlatformLoggers();
 292                                     return null;
 293                                 }
 294                             });
 295                     } catch (Exception ex) {
 296                         // System.err.println("Can't read logging configuration:");
 297                         // ex.printStackTrace();
 298                     }
 299                 }
 300             }
 301         }
 302     }
 303 
 304     /**
 305      * Adds an event listener to be invoked when the logging
 306      * properties are re-read. Adding multiple instances of
 307      * the same event Listener results in multiple entries
 308      * in the property event listener table.
 309      *
 310      * @param l  event listener
 311      * @exception  SecurityException  if a security manager exists and if
 312      *             the caller does not have LoggingPermission("control").
 313      * @exception NullPointerException if the PropertyChangeListener is null.
 314      */
 315     public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException {
 316         if (l == null) {
 317             throw new NullPointerException();
 318         }
 319         checkPermission();
 320         changes.addPropertyChangeListener(l);
 321     }
 322 
 323     /**
 324      * Removes an event listener for property change events.
 325      * If the same listener instance has been added to the listener table
 326      * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
 327      * then an equivalent number of
 328      * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
 329      * all instances of that listener from the listener table.
 330      * <P>
 331      * Returns silently if the given listener is not found.
 332      *
 333      * @param l  event listener (can be null)
 334      * @exception  SecurityException  if a security manager exists and if
 335      *             the caller does not have LoggingPermission("control").
 336      */
 337     public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException {
 338         checkPermission();
 339         changes.removePropertyChangeListener(l);
 340     }
 341 
 342     // Returns the LoggerContext for the user code (i.e. application or AppContext).
 343     // Loggers are isolated from each AppContext.
 344     private LoggerContext getUserContext() {
 345         LoggerContext context = null;
 346 
 347         SecurityManager sm = System.getSecurityManager();
 348         JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
 349         if (sm != null && javaAwtAccess != null) {
 350             synchronized (javaAwtAccess) {
 351                 // AppContext.getAppContext() returns the system AppContext if called
 352                 // from a system thread but Logger.getLogger might be called from
 353                 // an applet code. Instead, find the AppContext of the applet code
 354                 // from the execution stack.
 355                 Object ecx = javaAwtAccess.getExecutionContext();
 356                 if (ecx == null) {
 357                     // fall back to AppContext.getAppContext()
 358                     ecx = javaAwtAccess.getContext();
 359                 }
 360                 context = (LoggerContext)javaAwtAccess.get(ecx, LoggerContext.class);
 361                 if (context == null) {
 362                     if (javaAwtAccess.isMainAppContext()) {
 363                         context = userContext;
 364                     } else {
 365                         context = new LoggerContext();
 366                         context.addLocalLogger(manager.rootLogger);
 367                     }
 368                     javaAwtAccess.put(ecx, LoggerContext.class, context);
 369                 }
 370             }
 371         } else {
 372             context = userContext;
 373         }
 374         return context;
 375     }
 376 
 377     private List<LoggerContext> contexts() {
 378         List<LoggerContext> cxs = new ArrayList<>();
 379         cxs.add(systemContext);
 380         cxs.add(getUserContext());
 381         return cxs;
 382     }
 383 
 384     // Find or create a specified logger instance. If a logger has
 385     // already been created with the given name it is returned.
 386     // Otherwise a new logger instance is created and registered
 387     // in the LogManager global namespace.
 388     // This method will always return a non-null Logger object.
 389     // Synchronization is not required here. All synchronization for
 390     // adding a new Logger object is handled by addLogger().
 391     //
 392     // This method must delegate to the LogManager implementation to
 393     // add a new Logger or return the one that has been added previously
 394     // as a LogManager subclass may override the addLogger, getLogger,
 395     // readConfiguration, and other methods.
 396     Logger demandLogger(String name, String resourceBundleName) {
 397         Logger result = getLogger(name);
 398         if (result == null) {
 399             // only allocate the new logger once
 400             Logger newLogger = new Logger(name, resourceBundleName);
 401             do {
 402                 if (addLogger(newLogger)) {
 403                     // We successfully added the new Logger that we
 404                     // created above so return it without refetching.
 405                     return newLogger;
 406                 }
 407 
 408                 // We didn't add the new Logger that we created above
 409                 // because another thread added a Logger with the same
 410                 // name after our null check above and before our call
 411                 // to addLogger(). We have to refetch the Logger because
 412                 // addLogger() returns a boolean instead of the Logger
 413                 // reference itself. However, if the thread that created
 414                 // the other Logger is not holding a strong reference to
 415                 // the other Logger, then it is possible for the other
 416                 // Logger to be GC'ed after we saw it in addLogger() and
 417                 // before we can refetch it. If it has been GC'ed then
 418                 // we'll just loop around and try again.
 419                 result = getLogger(name);
 420             } while (result == null);
 421         }
 422         return result;
 423     }
 424 
 425     Logger demandSystemLogger(String name, String resourceBundleName) {
 426         return systemContext.demandLogger(name, resourceBundleName);
 427     }
 428 
 429     // LoggerContext maintains the logger namespace per context.
 430     // The default LogManager implementation has one system context and user
 431     // context.  The system context is used to maintain the namespace for
 432     // all system loggers and is queried by the system code.  If a system logger
 433     // doesn't exist in the user context, it'll also be added to the user context.
 434     // The user context is queried by the user code and all other loggers are
 435     // added in the user context.
 436     static class LoggerContext {
 437         // Table of named Loggers that maps names to Loggers.
 438         private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
 439         // Tree of named Loggers
 440         private final LogNode root;
 441 
 442         private LoggerContext() {
 443             this.root = new LogNode(null, this);
 444         }
 445 
 446         Logger demandLogger(String name, String resourceBundleName) {
 447             // a LogManager subclass may have its own implementation to add and
 448             // get a Logger.  So delegate to the LogManager to do the work.
 449             return manager.demandLogger(name, resourceBundleName);
 450         }
 451 
 452         synchronized Logger findLogger(String name) {
 453             LoggerWeakRef ref = namedLoggers.get(name);
 454             if (ref == null) {
 455                 return null;
 456             }
 457             Logger logger = ref.get();
 458             if (logger == null) {
 459                 // Hashtable holds stale weak reference
 460                 // to a logger which has been GC-ed.
 461                 removeLogger(name);
 462             }
 463             return logger;
 464         }
 465 
 466         // Add a logger to this context.  This method will only set its level
 467         // and process parent loggers.  It doesn't set its handlers.
 468         synchronized boolean addLocalLogger(Logger logger) {
 469             final String name = logger.getName();
 470             if (name == null) {
 471                 throw new NullPointerException();
 472             }
 473 
 474             // cleanup some Loggers that have been GC'ed
 475             manager.drainLoggerRefQueueBounded();
 476 
 477             LoggerWeakRef ref = namedLoggers.get(name);
 478             if (ref != null) {
 479                 if (ref.get() == null) {
 480                     // It's possible that the Logger was GC'ed after the
 481                     // drainLoggerRefQueueBounded() call above so allow
 482                     // a new one to be registered.
 483                     removeLogger(name);
 484                 } else {
 485                     // We already have a registered logger with the given name.
 486                     return false;
 487                 }
 488             }
 489 
 490             // We're adding a new logger.
 491             // Note that we are creating a weak reference here.
 492             ref = manager.new LoggerWeakRef(logger);
 493             namedLoggers.put(name, ref);
 494 
 495             // Apply any initial level defined for the new logger.
 496             Level level = manager.getLevelProperty(name + ".level", null);
 497             if (level != null) {
 498                 doSetLevel(logger, level);
 499             }
 500 
 501             processParentHandlers(logger, name);
 502 
 503             // Find the new node and its parent.
 504             LogNode node = getNode(name);
 505             node.loggerRef = ref;
 506             Logger parent = null;
 507             LogNode nodep = node.parent;
 508             while (nodep != null) {
 509                 LoggerWeakRef nodeRef = nodep.loggerRef;
 510                 if (nodeRef != null) {
 511                     parent = nodeRef.get();
 512                     if (parent != null) {
 513                         break;
 514                     }
 515                 }
 516                 nodep = nodep.parent;
 517             }
 518 
 519             if (parent != null) {
 520                 doSetParent(logger, parent);
 521             }
 522             // Walk over the children and tell them we are their new parent.
 523             node.walkAndSetParent(logger);
 524             // new LogNode is ready so tell the LoggerWeakRef about it
 525             ref.setNode(node);
 526             return true;
 527         }
 528 
 529         void removeLogger(String name) {
 530             namedLoggers.remove(name);
 531         }
 532 
 533         synchronized Enumeration<String> getLoggerNames() {
 534             return namedLoggers.keys();
 535         }
 536 
 537         // If logger.getUseParentHandlers() returns 'true' and any of the logger's
 538         // parents have levels or handlers defined, make sure they are instantiated.
 539         private void processParentHandlers(final Logger logger, final String name) {
 540             AccessController.doPrivileged(new PrivilegedAction<Void>() {
 541                 public Void run() {
 542                     if (logger != manager.rootLogger) {
 543                         boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true);
 544                         if (!useParent) {
 545                             logger.setUseParentHandlers(false);
 546                         }
 547                     }
 548                     return null;
 549                 }
 550             });
 551 
 552             int ix = 1;
 553             for (;;) {
 554                 int ix2 = name.indexOf(".", ix);
 555                 if (ix2 < 0) {
 556                     break;
 557                 }
 558                 String pname = name.substring(0, ix2);
 559                 if (manager.getProperty(pname + ".level") != null ||
 560                     manager.getProperty(pname + ".handlers") != null) {
 561                     // This pname has a level/handlers definition.
 562                     // Make sure it exists.
 563                     demandLogger(pname, null);
 564                 }
 565                 ix = ix2+1;
 566             }
 567         }
 568 
 569         // Gets a node in our tree of logger nodes.
 570         // If necessary, create it.
 571         LogNode getNode(String name) {
 572             if (name == null || name.equals("")) {
 573                 return root;
 574             }
 575             LogNode node = root;
 576             while (name.length() > 0) {
 577                 int ix = name.indexOf(".");
 578                 String head;
 579                 if (ix > 0) {
 580                     head = name.substring(0, ix);
 581                     name = name.substring(ix + 1);
 582                 } else {
 583                     head = name;
 584                     name = "";
 585                 }
 586                 if (node.children == null) {
 587                     node.children = new HashMap<>();
 588                 }
 589                 LogNode child = node.children.get(head);
 590                 if (child == null) {
 591                     child = new LogNode(node, this);
 592                     node.children.put(head, child);
 593                 }
 594                 node = child;
 595             }
 596             return node;
 597         }
 598     }
 599 
 600     static class SystemLoggerContext extends LoggerContext {
 601         // Add a system logger in the system context's namespace as well as
 602         // in the LogManager's namespace if not exist so that there is only
 603         // one single logger of the given name.  System loggers are visible
 604         // to applications unless a logger of the same name has been added.
 605         Logger demandLogger(String name, String resourceBundleName) {
 606             Logger result = findLogger(name);
 607             if (result == null) {
 608                 // only allocate the new system logger once
 609                 Logger newLogger = new Logger(name, resourceBundleName);
 610                 do {
 611                     if (addLocalLogger(newLogger)) {
 612                         // We successfully added the new Logger that we
 613                         // created above so return it without refetching.
 614                         result = newLogger;
 615                     } else {
 616                         // We didn't add the new Logger that we created above
 617                         // because another thread added a Logger with the same
 618                         // name after our null check above and before our call
 619                         // to addLogger(). We have to refetch the Logger because
 620                         // addLogger() returns a boolean instead of the Logger
 621                         // reference itself. However, if the thread that created
 622                         // the other Logger is not holding a strong reference to
 623                         // the other Logger, then it is possible for the other
 624                         // Logger to be GC'ed after we saw it in addLogger() and
 625                         // before we can refetch it. If it has been GC'ed then
 626                         // we'll just loop around and try again.
 627                         result = findLogger(name);
 628                     }
 629                 } while (result == null);
 630             }
 631             // Add the system logger to the LogManager's namespace if not exists
 632             // The LogManager will set its handlers via the LogManager.addLogger method.
 633             if (!manager.addLogger(result) && result.getHandlers().length == 0) {
 634                 // if logger already exists but handlers not set
 635                 final Logger l = manager.getLogger(name);
 636                 final Logger logger = result;
 637                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
 638                     public Void run() {
 639                         for (Handler hdl : l.getHandlers()) {
 640                             logger.addHandler(hdl);
 641                         }
 642                         return null;
 643                     }
 644                 });
 645             }
 646             return result;
 647         }
 648     }
 649 
 650     // Add new per logger handlers.
 651     // We need to raise privilege here. All our decisions will
 652     // be made based on the logging configuration, which can
 653     // only be modified by trusted code.
 654     private void loadLoggerHandlers(final Logger logger, final String name,
 655                                     final String handlersPropertyName)
 656     {
 657         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 658             public Object run() {
 659                 String names[] = parseClassNames(handlersPropertyName);
 660                 for (int i = 0; i < names.length; i++) {
 661                     String word = names[i];
 662                     try {
 663                         Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
 664                         Handler hdl = (Handler) clz.newInstance();
 665                         // Check if there is a property defining the
 666                         // this handler's level.
 667                         String levs = getProperty(word + ".level");
 668                         if (levs != null) {
 669                             Level l = Level.findLevel(levs);
 670                             if (l != null) {
 671                                 hdl.setLevel(l);
 672                             } else {
 673                                 // Probably a bad level. Drop through.
 674                                 System.err.println("Can't set level for " + word);
 675                             }
 676                         }
 677                         // Add this Handler to the logger
 678                         logger.addHandler(hdl);
 679                     } catch (Exception ex) {
 680                         System.err.println("Can't load log handler \"" + word + "\"");
 681                         System.err.println("" + ex);
 682                         ex.printStackTrace();
 683                     }
 684                 }
 685                 return null;
 686             }
 687         });
 688     }
 689 
 690 
 691     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
 692     // that have been GC'ed.
 693     private final ReferenceQueue<Logger> loggerRefQueue
 694         = new ReferenceQueue<>();
 695 
 696     // Package-level inner class.
 697     // Helper class for managing WeakReferences to Logger objects.
 698     //
 699     // LogManager.namedLoggers
 700     //     - has weak references to all named Loggers
 701     //     - namedLoggers keeps the LoggerWeakRef objects for the named
 702     //       Loggers around until we can deal with the book keeping for
 703     //       the named Logger that is being GC'ed.
 704     // LogManager.LogNode.loggerRef
 705     //     - has a weak reference to a named Logger
 706     //     - the LogNode will also keep the LoggerWeakRef objects for
 707     //       the named Loggers around; currently LogNodes never go away.
 708     // Logger.kids
 709     //     - has a weak reference to each direct child Logger; this
 710     //       includes anonymous and named Loggers
 711     //     - anonymous Loggers are always children of the rootLogger
 712     //       which is a strong reference; rootLogger.kids keeps the
 713     //       LoggerWeakRef objects for the anonymous Loggers around
 714     //       until we can deal with the book keeping.
 715     //
 716     final class LoggerWeakRef extends WeakReference<Logger> {
 717         private String                name;       // for namedLoggers cleanup
 718         private LogNode               node;       // for loggerRef cleanup
 719         private WeakReference<Logger> parentRef;  // for kids cleanup
 720 
 721         LoggerWeakRef(Logger logger) {
 722             super(logger, loggerRefQueue);
 723 
 724             name = logger.getName();  // save for namedLoggers cleanup
 725         }
 726 
 727         // dispose of this LoggerWeakRef object
 728         void dispose() {
 729             if (node != null) {
 730                 // if we have a LogNode, then we were a named Logger
 731                 // so clear namedLoggers weak ref to us
 732                 node.context.removeLogger(name);
 733                 name = null;  // clear our ref to the Logger's name
 734 
 735                 node.loggerRef = null;  // clear LogNode's weak ref to us
 736                 node = null;            // clear our ref to LogNode
 737             }
 738 
 739             if (parentRef != null) {
 740                 // this LoggerWeakRef has or had a parent Logger
 741                 Logger parent = parentRef.get();
 742                 if (parent != null) {
 743                     // the parent Logger is still there so clear the
 744                     // parent Logger's weak ref to us
 745                     parent.removeChildLogger(this);
 746                 }
 747                 parentRef = null;  // clear our weak ref to the parent Logger
 748             }
 749         }
 750 
 751         // set the node field to the specified value
 752         void setNode(LogNode node) {
 753             this.node = node;
 754         }
 755 
 756         // set the parentRef field to the specified value
 757         void setParentRef(WeakReference<Logger> parentRef) {
 758             this.parentRef = parentRef;
 759         }
 760     }
 761 
 762     // Package-level method.
 763     // Drain some Logger objects that have been GC'ed.
 764     //
 765     // drainLoggerRefQueueBounded() is called by addLogger() below
 766     // and by Logger.getAnonymousLogger(String) so we'll drain up to
 767     // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
 768     //
 769     // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
 770     // us about a 50/50 mix in increased weak ref counts versus
 771     // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
 772     // Here are stats for cleaning up sets of 400 anonymous Loggers:
 773     //   - test duration 1 minute
 774     //   - sample size of 125 sets of 400
 775     //   - average: 1.99 ms
 776     //   - minimum: 0.57 ms
 777     //   - maximum: 25.3 ms
 778     //
 779     // The same config gives us a better decreased weak ref count
 780     // than increased weak ref count in the LoggerWeakRefLeak test.
 781     // Here are stats for cleaning up sets of 400 named Loggers:
 782     //   - test duration 2 minutes
 783     //   - sample size of 506 sets of 400
 784     //   - average: 0.57 ms
 785     //   - minimum: 0.02 ms
 786     //   - maximum: 10.9 ms
 787     //
 788     private final static int MAX_ITERATIONS = 400;
 789     final synchronized void drainLoggerRefQueueBounded() {
 790         for (int i = 0; i < MAX_ITERATIONS; i++) {
 791             if (loggerRefQueue == null) {
 792                 // haven't finished loading LogManager yet
 793                 break;
 794             }
 795 
 796             LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
 797             if (ref == null) {
 798                 break;
 799             }
 800             // a Logger object has been GC'ed so clean it up
 801             ref.dispose();
 802         }
 803     }
 804 
 805     /**
 806      * Add a named logger.  This does nothing and returns false if a logger
 807      * with the same name is already registered.
 808      * <p>
 809      * The Logger factory methods call this method to register each
 810      * newly created Logger.
 811      * <p>
 812      * The application should retain its own reference to the Logger
 813      * object to avoid it being garbage collected.  The LogManager
 814      * may only retain a weak reference.
 815      *
 816      * @param   logger the new logger.
 817      * @return  true if the argument logger was registered successfully,
 818      *          false if a logger of that name already exists.
 819      * @exception NullPointerException if the logger name is null.
 820      */
 821     public boolean addLogger(Logger logger) {
 822         final String name = logger.getName();
 823         if (name == null) {
 824             throw new NullPointerException();
 825         }
 826         LoggerContext cx = getUserContext();
 827         if (cx.addLocalLogger(logger)) {
 828             // Do we have a per logger handler too?
 829             // Note: this will add a 200ms penalty
 830             loadLoggerHandlers(logger, name, name + ".handlers");
 831             return true;
 832         } else {
 833             return false;
 834         }
 835     }
 836 
 837     // Private method to set a level on a logger.
 838     // If necessary, we raise privilege before doing the call.
 839     private static void doSetLevel(final Logger logger, final Level level) {
 840         SecurityManager sm = System.getSecurityManager();
 841         if (sm == null) {
 842             // There is no security manager, so things are easy.
 843             logger.setLevel(level);
 844             return;
 845         }
 846         // There is a security manager.  Raise privilege before
 847         // calling setLevel.
 848         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 849             public Object run() {
 850                 logger.setLevel(level);
 851                 return null;
 852             }});
 853     }
 854 
 855     // Private method to set a parent on a logger.
 856     // If necessary, we raise privilege before doing the setParent call.
 857     private static void doSetParent(final Logger logger, final Logger parent) {
 858         SecurityManager sm = System.getSecurityManager();
 859         if (sm == null) {
 860             // There is no security manager, so things are easy.
 861             logger.setParent(parent);
 862             return;
 863         }
 864         // There is a security manager.  Raise privilege before
 865         // calling setParent.
 866         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 867             public Object run() {
 868                 logger.setParent(parent);
 869                 return null;
 870             }});
 871     }
 872 
 873     /**
 874      * Method to find a named logger.
 875      * <p>
 876      * Note that since untrusted code may create loggers with
 877      * arbitrary names this method should not be relied on to
 878      * find Loggers for security sensitive logging.
 879      * It is also important to note that the Logger associated with the
 880      * String {@code name} may be garbage collected at any time if there
 881      * is no strong reference to the Logger. The caller of this method
 882      * must check the return value for null in order to properly handle
 883      * the case where the Logger has been garbage collected.
 884      * <p>
 885      * @param name name of the logger
 886      * @return  matching logger or null if none is found
 887      */
 888     public Logger getLogger(String name) {
 889         return getUserContext().findLogger(name);
 890     }
 891 
 892     /**
 893      * Get an enumeration of known logger names.
 894      * <p>
 895      * Note:  Loggers may be added dynamically as new classes are loaded.
 896      * This method only reports on the loggers that are currently registered.
 897      * It is also important to note that this method only returns the name
 898      * of a Logger, not a strong reference to the Logger itself.
 899      * The returned String does nothing to prevent the Logger from being
 900      * garbage collected. In particular, if the returned name is passed
 901      * to {@code LogManager.getLogger()}, then the caller must check the
 902      * return value from {@code LogManager.getLogger()} for null to properly
 903      * handle the case where the Logger has been garbage collected in the
 904      * time since its name was returned by this method.
 905      * <p>
 906      * @return  enumeration of logger name strings
 907      */
 908     public Enumeration<String> getLoggerNames() {
 909         return getUserContext().getLoggerNames();
 910     }
 911 
 912     /**
 913      * Reinitialize the logging properties and reread the logging configuration.
 914      * <p>
 915      * The same rules are used for locating the configuration properties
 916      * as are used at startup.  So normally the logging properties will
 917      * be re-read from the same file that was used at startup.
 918      * <P>
 919      * Any log level definitions in the new configuration file will be
 920      * applied using Logger.setLevel(), if the target Logger exists.
 921      * <p>
 922      * A PropertyChangeEvent will be fired after the properties are read.
 923      *
 924      * @exception  SecurityException  if a security manager exists and if
 925      *             the caller does not have LoggingPermission("control").
 926      * @exception  IOException if there are IO problems reading the configuration.
 927      */
 928     public void readConfiguration() throws IOException, SecurityException {
 929         checkPermission();
 930 
 931         // if a configuration class is specified, load it and use it.
 932         String cname = System.getProperty("java.util.logging.config.class");
 933         if (cname != null) {
 934             try {
 935                 // Instantiate the named class.  It is its constructor's
 936                 // responsibility to initialize the logging configuration, by
 937                 // calling readConfiguration(InputStream) with a suitable stream.
 938                 try {
 939                     Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 940                     clz.newInstance();
 941                     return;
 942                 } catch (ClassNotFoundException ex) {
 943                     Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 944                     clz.newInstance();
 945                     return;
 946                 }
 947             } catch (Exception ex) {
 948                 System.err.println("Logging configuration class \"" + cname + "\" failed");
 949                 System.err.println("" + ex);
 950                 // keep going and useful config file.
 951             }
 952         }
 953 
 954         String fname = System.getProperty("java.util.logging.config.file");
 955         if (fname == null) {
 956             fname = System.getProperty("java.home");
 957             if (fname == null) {
 958                 throw new Error("Can't find java.home ??");
 959             }
 960             File f = new File(fname, "lib");
 961             f = new File(f, "logging.properties");
 962             fname = f.getCanonicalPath();
 963         }
 964         InputStream in = new FileInputStream(fname);
 965         BufferedInputStream bin = new BufferedInputStream(in);
 966         try {
 967             readConfiguration(bin);
 968         } finally {
 969             if (in != null) {
 970                 in.close();
 971             }
 972         }
 973     }
 974 
 975     /**
 976      * Reset the logging configuration.
 977      * <p>
 978      * For all named loggers, the reset operation removes and closes
 979      * all Handlers and (except for the root logger) sets the level
 980      * to null.  The root logger's level is set to Level.INFO.
 981      *
 982      * @exception  SecurityException  if a security manager exists and if
 983      *             the caller does not have LoggingPermission("control").
 984      */
 985 
 986     public void reset() throws SecurityException {
 987         checkPermission();
 988         synchronized (this) {
 989             props = new Properties();
 990             // Since we are doing a reset we no longer want to initialize
 991             // the global handlers, if they haven't been initialized yet.
 992             initializedGlobalHandlers = true;
 993         }
 994         for (LoggerContext cx : contexts()) {
 995             Enumeration<String> enum_ = cx.getLoggerNames();
 996             while (enum_.hasMoreElements()) {
 997                 String name = enum_.nextElement();
 998                 Logger logger = cx.findLogger(name);
 999                 if (logger != null) {
1000                     resetLogger(logger);
1001                 }
1002             }
1003         }
1004     }
1005 
1006     // Private method to reset an individual target logger.
1007     private void resetLogger(Logger logger) {
1008         // Close all the Logger's handlers.
1009         Handler[] targets = logger.getHandlers();
1010         for (int i = 0; i < targets.length; i++) {
1011             Handler h = targets[i];
1012             logger.removeHandler(h);
1013             try {
1014                 h.close();
1015             } catch (Exception ex) {
1016                 // Problems closing a handler?  Keep going...
1017             }
1018         }
1019         String name = logger.getName();
1020         if (name != null && name.equals("")) {
1021             // This is the root logger.
1022             logger.setLevel(defaultLevel);
1023         } else {
1024             logger.setLevel(null);
1025         }
1026     }
1027 
1028     // get a list of whitespace separated classnames from a property.
1029     private String[] parseClassNames(String propertyName) {
1030         String hands = getProperty(propertyName);
1031         if (hands == null) {
1032             return new String[0];
1033         }
1034         hands = hands.trim();
1035         int ix = 0;
1036         Vector<String> result = new Vector<>();
1037         while (ix < hands.length()) {
1038             int end = ix;
1039             while (end < hands.length()) {
1040                 if (Character.isWhitespace(hands.charAt(end))) {
1041                     break;
1042                 }
1043                 if (hands.charAt(end) == ',') {
1044                     break;
1045                 }
1046                 end++;
1047             }
1048             String word = hands.substring(ix, end);
1049             ix = end+1;
1050             word = word.trim();
1051             if (word.length() == 0) {
1052                 continue;
1053             }
1054             result.add(word);
1055         }
1056         return result.toArray(new String[result.size()]);
1057     }
1058 
1059     /**
1060      * Reinitialize the logging properties and reread the logging configuration
1061      * from the given stream, which should be in java.util.Properties format.
1062      * A PropertyChangeEvent will be fired after the properties are read.
1063      * <p>
1064      * Any log level definitions in the new configuration file will be
1065      * applied using Logger.setLevel(), if the target Logger exists.
1066      *
1067      * @param ins       stream to read properties from
1068      * @exception  SecurityException  if a security manager exists and if
1069      *             the caller does not have LoggingPermission("control").
1070      * @exception  IOException if there are problems reading from the stream.
1071      */
1072     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1073         checkPermission();
1074         reset();
1075 
1076         // Load the properties
1077         props.load(ins);
1078         // Instantiate new configuration objects.
1079         String names[] = parseClassNames("config");
1080 
1081         for (int i = 0; i < names.length; i++) {
1082             String word = names[i];
1083             try {
1084                 Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
1085                 clz.newInstance();
1086             } catch (Exception ex) {
1087                 System.err.println("Can't load config class \"" + word + "\"");
1088                 System.err.println("" + ex);
1089                 // ex.printStackTrace();
1090             }
1091         }
1092 
1093         // Set levels on any pre-existing loggers, based on the new properties.
1094         setLevelsOnExistingLoggers();
1095 
1096         // Notify any interested parties that our properties have changed.
1097         changes.firePropertyChange(null, null, null);
1098 
1099         // Note that we need to reinitialize global handles when
1100         // they are first referenced.
1101         synchronized (this) {
1102             initializedGlobalHandlers = false;
1103         }
1104     }
1105 
1106     /**
1107      * Get the value of a logging property.
1108      * The method returns null if the property is not found.
1109      * @param name      property name
1110      * @return          property value
1111      */
1112     public String getProperty(String name) {
1113         return props.getProperty(name);
1114     }
1115 
1116     // Package private method to get a String property.
1117     // If the property is not defined we return the given
1118     // default value.
1119     String getStringProperty(String name, String defaultValue) {
1120         String val = getProperty(name);
1121         if (val == null) {
1122             return defaultValue;
1123         }
1124         return val.trim();
1125     }
1126 
1127     // Package private method to get an integer property.
1128     // If the property is not defined or cannot be parsed
1129     // we return the given default value.
1130     int getIntProperty(String name, int defaultValue) {
1131         String val = getProperty(name);
1132         if (val == null) {
1133             return defaultValue;
1134         }
1135         try {
1136             return Integer.parseInt(val.trim());
1137         } catch (Exception ex) {
1138             return defaultValue;
1139         }
1140     }
1141 
1142     // Package private method to get a boolean property.
1143     // If the property is not defined or cannot be parsed
1144     // we return the given default value.
1145     boolean getBooleanProperty(String name, boolean defaultValue) {
1146         String val = getProperty(name);
1147         if (val == null) {
1148             return defaultValue;
1149         }
1150         val = val.toLowerCase();
1151         if (val.equals("true") || val.equals("1")) {
1152             return true;
1153         } else if (val.equals("false") || val.equals("0")) {
1154             return false;
1155         }
1156         return defaultValue;
1157     }
1158 
1159     // Package private method to get a Level property.
1160     // If the property is not defined or cannot be parsed
1161     // we return the given default value.
1162     Level getLevelProperty(String name, Level defaultValue) {
1163         String val = getProperty(name);
1164         if (val == null) {
1165             return defaultValue;
1166         }
1167         Level l = Level.findLevel(val.trim());
1168         return l != null ? l : defaultValue;
1169     }
1170 
1171     // Package private method to get a filter property.
1172     // We return an instance of the class named by the "name"
1173     // property. If the property is not defined or has problems
1174     // we return the defaultValue.
1175     Filter getFilterProperty(String name, Filter defaultValue) {
1176         String val = getProperty(name);
1177         try {
1178             if (val != null) {
1179                 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
1180                 return (Filter) clz.newInstance();
1181             }
1182         } catch (Exception ex) {
1183             // We got one of a variety of exceptions in creating the
1184             // class or creating an instance.
1185             // Drop through.
1186         }
1187         // We got an exception.  Return the defaultValue.
1188         return defaultValue;
1189     }
1190 
1191 
1192     // Package private method to get a formatter property.
1193     // We return an instance of the class named by the "name"
1194     // property. If the property is not defined or has problems
1195     // we return the defaultValue.
1196     Formatter getFormatterProperty(String name, Formatter defaultValue) {
1197         String val = getProperty(name);
1198         try {
1199             if (val != null) {
1200                 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
1201                 return (Formatter) clz.newInstance();
1202             }
1203         } catch (Exception ex) {
1204             // We got one of a variety of exceptions in creating the
1205             // class or creating an instance.
1206             // Drop through.
1207         }
1208         // We got an exception.  Return the defaultValue.
1209         return defaultValue;
1210     }
1211 
1212     // Private method to load the global handlers.
1213     // We do the real work lazily, when the global handlers
1214     // are first used.
1215     private synchronized void initializeGlobalHandlers() {
1216         if (initializedGlobalHandlers) {
1217             return;
1218         }
1219 
1220         initializedGlobalHandlers = true;
1221 
1222         if (deathImminent) {
1223             // Aaargh...
1224             // The VM is shutting down and our exit hook has been called.
1225             // Avoid allocating global handlers.
1226             return;
1227         }
1228         loadLoggerHandlers(rootLogger, null, "handlers");
1229     }
1230 
1231     private final Permission controlPermission = new LoggingPermission("control", null);
1232 
1233     void checkPermission() {
1234         SecurityManager sm = System.getSecurityManager();
1235         if (sm != null)
1236             sm.checkPermission(controlPermission);
1237     }
1238 
1239     /**
1240      * Check that the current context is trusted to modify the logging
1241      * configuration.  This requires LoggingPermission("control").
1242      * <p>
1243      * If the check fails we throw a SecurityException, otherwise
1244      * we return normally.
1245      *
1246      * @exception  SecurityException  if a security manager exists and if
1247      *             the caller does not have LoggingPermission("control").
1248      */
1249     public void checkAccess() throws SecurityException {
1250         checkPermission();
1251     }
1252 
1253     // Nested class to represent a node in our tree of named loggers.
1254     private static class LogNode {
1255         HashMap<String,LogNode> children;
1256         LoggerWeakRef loggerRef;
1257         LogNode parent;
1258         final LoggerContext context;
1259 
1260         LogNode(LogNode parent, LoggerContext context) {
1261             this.parent = parent;
1262             this.context = context;
1263         }
1264 
1265         // Recursive method to walk the tree below a node and set
1266         // a new parent logger.
1267         void walkAndSetParent(Logger parent) {
1268             if (children == null) {
1269                 return;
1270             }
1271             Iterator<LogNode> values = children.values().iterator();
1272             while (values.hasNext()) {
1273                 LogNode node = values.next();
1274                 LoggerWeakRef ref = node.loggerRef;
1275                 Logger logger = (ref == null) ? null : ref.get();
1276                 if (logger == null) {
1277                     node.walkAndSetParent(parent);
1278                 } else {
1279                     doSetParent(logger, parent);
1280                 }
1281             }
1282         }
1283     }
1284 
1285     // We use a subclass of Logger for the root logger, so
1286     // that we only instantiate the global handlers when they
1287     // are first needed.
1288     private class RootLogger extends Logger {
1289         private RootLogger() {
1290             super("", null);
1291             setLevel(defaultLevel);
1292         }
1293 
1294         public void log(LogRecord record) {
1295             // Make sure that the global handlers have been instantiated.
1296             initializeGlobalHandlers();
1297             super.log(record);
1298         }
1299 
1300         public void addHandler(Handler h) {
1301             initializeGlobalHandlers();
1302             super.addHandler(h);
1303         }
1304 
1305         public void removeHandler(Handler h) {
1306             initializeGlobalHandlers();
1307             super.removeHandler(h);
1308         }
1309 
1310         public Handler[] getHandlers() {
1311             initializeGlobalHandlers();
1312             return super.getHandlers();
1313         }
1314     }
1315 
1316 
1317     // Private method to be called when the configuration has
1318     // changed to apply any level settings to any pre-existing loggers.
1319     synchronized private void setLevelsOnExistingLoggers() {
1320         Enumeration<?> enum_ = props.propertyNames();
1321         while (enum_.hasMoreElements()) {
1322             String key = (String)enum_.nextElement();
1323             if (!key.endsWith(".level")) {
1324                 // Not a level definition.
1325                 continue;
1326             }
1327             int ix = key.length() - 6;
1328             String name = key.substring(0, ix);
1329             Level level = getLevelProperty(key, null);
1330             if (level == null) {
1331                 System.err.println("Bad level value for property: " + key);
1332                 continue;
1333             }
1334             for (LoggerContext cx : contexts()) {
1335                 Logger l = cx.findLogger(name);
1336                 if (l == null) {
1337                     continue;
1338                 }
1339                 l.setLevel(level);
1340             }
1341         }
1342     }
1343 
1344     // Management Support
1345     private static LoggingMXBean loggingMXBean = null;
1346     /**
1347      * String representation of the
1348      * {@link javax.management.ObjectName} for the management interface
1349      * for the logging facility.
1350      *
1351      * @see java.lang.management.PlatformLoggingMXBean
1352      * @see java.util.logging.LoggingMXBean
1353      *
1354      * @since 1.5
1355      */
1356     public final static String LOGGING_MXBEAN_NAME
1357         = "java.util.logging:type=Logging";
1358 
1359     /**
1360      * Returns <tt>LoggingMXBean</tt> for managing loggers.
1361      * An alternative way to manage loggers is through the
1362      * {@link java.lang.management.PlatformLoggingMXBean} interface
1363      * that can be obtained by calling:
1364      * <pre>
1365      *     PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
1366      *         ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
1367      * </pre>
1368      *
1369      * @return a {@link LoggingMXBean} object.
1370      *
1371      * @see java.lang.management.PlatformLoggingMXBean
1372      * @since 1.5
1373      */
1374     public static synchronized LoggingMXBean getLoggingMXBean() {
1375         if (loggingMXBean == null) {
1376             loggingMXBean =  new Logging();
1377         }
1378         return loggingMXBean;
1379     }
1380 
1381 }