1 /*
   2  * Copyright (c) 2014, 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 package sun.util.logger;
  27 
  28 import java.security.AccessControlContext;
  29 import java.security.AccessController;
  30 import java.security.PrivilegedAction;
  31 import java.util.HashMap;
  32 import java.util.Iterator;
  33 import java.util.Map;
  34 import java.util.ResourceBundle;
  35 import java.util.ServiceLoader;
  36 import java.util.function.BooleanSupplier;
  37 import java.util.function.Function;
  38 import java.util.function.Supplier;
  39 import java.lang.System.LoggerFinder;
  40 import java.lang.System.Logger;
  41 import java.lang.System.Logger.Level;
  42 import java.lang.ref.WeakReference;
  43 import java.util.concurrent.ExecutionException;
  44 import java.util.concurrent.ExecutorService;
  45 import java.util.concurrent.LinkedBlockingQueue;
  46 import java.util.concurrent.ThreadFactory;
  47 import java.util.concurrent.ThreadPoolExecutor;
  48 import java.util.concurrent.TimeUnit;
  49 import sun.misc.InnocuousThread;
  50 import sun.misc.VM;
  51 import sun.util.logging.PlatformLogger;
  52 import sun.util.logging.PlatformLoggerBridge;
  53 import sun.util.logger.LazyLoggers.LazyLoggerAccessor;
  54 import sun.util.logging.ConfigurableLoggerBridge;
  55 
  56 /**
  57  * The BootstrapLogger class handles all the logic needed by Lazy Loggers
  58  * to delay the creation of System.Logger instances until the VM is booted.
  59  * By extension - it also contains the logic that will delay the creation
  60  * of JUL Loggers until the LogManager is initialized by the application, in
  61  * the common case where JUL is the default and there is no custom JUL
  62  * configuration.
  63  *
  64  * A BootstrapLogger instance is both a Logger and a
  65  * PlatformLoggerBridge instance, which will put all Log messages in a queue
  66  * until the VM is booted.
  67  * Once the VM is booted, it obtain the real System.Logger instance from the
  68  * LoggerFinder and flushes the message to the queue.
  69  *
  70  * There are a few caveat:
  71  *  - the queue may not be flush until the next message is logged after
  72  *    the VM is booted
  73  *  - while the BootstrapLogger is active, the default implementation
  74  *    for all convenience methods is used
  75  *  - PlatformLogger.setLevel calls are ignored
  76  *
  77  * @since 1.9
  78  *
  79  */
  80 public final class BootstrapLogger implements Logger, PlatformLoggerBridge,
  81         ConfigurableLoggerBridge {
  82 
  83     // We use the BootstrapExecutors class to submit delayed messages
  84     // to an indepedent InnocuousThread which will ensure that
  85     // delayed log events will be clearly identified as messages that have
  86     // been delayed during the boot sequence.
  87     private static class BootstrapExecutors implements ThreadFactory {
  88 
  89         // Maybe that should be made configurable with system properties.
  90         static final long KEEP_EXECUTOR_ALIVE_SECONDS = 30;
  91 
  92         // The BootstrapMessageLoggerTask is a Runnable which keeps
  93         // a hard ref to the ExecutorService that owns it.
  94         // This ensure that the ExecutorService is not gc'ed until the thread
  95         // has stopped running.
  96         private static class BootstrapMessageLoggerTask implements Runnable {
  97             ExecutorService owner;
  98             Runnable run;
  99             public BootstrapMessageLoggerTask(ExecutorService owner, Runnable r) {
 100                 this.owner = owner;
 101                 this.run = r;
 102             }
 103             @Override
 104             public void run() {
 105                 try {
 106                     run.run();
 107                 } finally {
 108                     owner = null; // allow the ExecutorService to be gced.
 109                 }
 110             }
 111         }
 112 
 113         private static volatile WeakReference<ExecutorService> executorRef;
 114         private static ExecutorService getExecutor() {
 115             WeakReference<ExecutorService> ref = executorRef;
 116             ExecutorService executor = ref == null ? null : ref.get();
 117             if (executor != null) return executor;
 118             synchronized (BootstrapExecutors.class) {
 119                 ref = executorRef;
 120                 executor = ref == null ? null : ref.get();
 121                 if (executor == null) {
 122                     executor = new ThreadPoolExecutor(0, 1,
 123                             KEEP_EXECUTOR_ALIVE_SECONDS, TimeUnit.SECONDS,
 124                             new LinkedBlockingQueue<>(), new BootstrapExecutors());
 125                 }
 126                 // The executor service will be elligible for gc
 127                 // KEEP_EXECUTOR_ALIVE_SECONDS seconds (30s)
 128                 // after the execution of its last pending task.
 129                 executorRef = new WeakReference<>(executor);
 130                 return executorRef.get();
 131             }
 132         }
 133 
 134         @Override
 135         public Thread newThread(Runnable r) {
 136             ExecutorService owner = getExecutor();
 137             Thread thread = AccessController.doPrivileged(new PrivilegedAction<Thread>() {
 138                 @Override
 139                 public Thread run() {
 140                     Thread t = new InnocuousThread(new BootstrapMessageLoggerTask(owner, r));
 141                     t.setName("BootstrapMessageLoggerTask-"+t.getName());
 142                     return t;
 143                 }
 144             }, null, new RuntimePermission("enableContextClassLoaderOverride"));
 145             thread.setDaemon(true);
 146             return thread;
 147         }
 148 
 149         static void submit(Runnable r) {
 150             getExecutor().execute(r);
 151         }
 152 
 153         static void join(Runnable r) {
 154             try {
 155                 getExecutor().submit(r).get();
 156             } catch (InterruptedException | ExecutionException ex) {
 157                 // should not happen
 158                 ex.printStackTrace(System.err);
 159             }
 160         }
 161 
 162         // This is used by tests.
 163         static void awaitPendingTasks() {
 164             WeakReference<ExecutorService> ref = executorRef;
 165             ExecutorService executor = ref == null ? null : ref.get();
 166             if (ref == null) {
 167                 synchronized(BootstrapExecutors.class) {
 168                     ref = executorRef;
 169                     executor = ref == null ? null : ref.get();
 170                 }
 171             }
 172             if (executor != null) {
 173                 // since our executor uses a FIFO and has a single thread
 174                 // then awaiting the execution of its pending tasks can be done
 175                 // simply by registering a new task and waiting until it
 176                 // completes. This of course would not work if we were using
 177                 // several threads, but we don't.
 178                 join(()->{});
 179             }
 180         }
 181 
 182         // This is used by tests.
 183         static boolean isAlive() {
 184             WeakReference<ExecutorService> ref = executorRef;
 185             ExecutorService executor = ref == null ? null : ref.get();
 186             if (executor != null) return true;
 187             synchronized (BootstrapExecutors.class) {
 188                 ref = executorRef;
 189                 executor = ref == null ? null : ref.get();
 190                 return executor != null;
 191             }
 192         }
 193     }
 194 
 195     // The accessor in which this logger is temporarilly set.
 196     final LazyLoggerAccessor holder;
 197 
 198     // The pending log event queue. The first event is the head, and
 199     // new events are added at the tail
 200     LogEvent head, tail;
 201 
 202     BootstrapLogger(LazyLoggerAccessor holder) {
 203         this.holder = holder;
 204     }
 205 
 206     // Temporary data object storing log events
 207     // It would be nice to use a Consumer<Logger> instead of a LogEvent.
 208     // This way we could simply do things like:
 209     //    push((logger) -> logger.log(level, msg));
 210     // Unfortunately, if we come to here it means we are in the bootsraping
 211     // phase where using lambdas is not safe yet - so we have to use a
 212     // a data object instead...
 213     //
 214     static final class LogEvent {
 215         // only one of these two levels should be non null
 216         final Level level;
 217         final PlatformLogger.Level platformLevel;
 218 
 219         final ResourceBundle bundle;
 220         final String msg;
 221         final Throwable thrown;
 222         final Object[] params;
 223         final Supplier<String> msgSupplier;
 224         final String sourceClass;
 225         final String sourceMethod;
 226         final long timeMillis;
 227         final long nanoAdjustment;
 228 
 229         // because logging a message may entail calling toString() on
 230         // the parameters etc... we need to store the context of the
 231         // caller who logged the message - so that we can reuse it when
 232         // we finally log the message.
 233         final AccessControlContext acc;
 234 
 235         // The next event in the queue
 236         LogEvent next;
 237 
 238         private LogEvent(Level level, ResourceBundle bundle, String msg,
 239                 Throwable thrown, Object[] params) {
 240             this.acc = AccessController.getContext();
 241             this.timeMillis = System.currentTimeMillis();
 242             this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
 243             this.level = level;
 244             this.platformLevel = null;
 245             this.bundle = bundle;
 246             this.msg = msg;
 247             this.msgSupplier = null;
 248             this.thrown = thrown;
 249             this.params = params;
 250             this.sourceClass = null;
 251             this.sourceMethod = null;
 252         }
 253 
 254         private LogEvent(Level level, Supplier<String> msgSupplier,
 255                 Throwable thrown, Object[] params) {
 256             this.acc = AccessController.getContext();
 257             this.timeMillis = System.currentTimeMillis();
 258             this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
 259             this.level = level;
 260             this.platformLevel = null;
 261             this.bundle = null;
 262             this.msg = null;
 263             this.msgSupplier = msgSupplier;
 264             this.thrown = thrown;
 265             this.params = params;
 266             this.sourceClass = null;
 267             this.sourceMethod = null;
 268         }
 269 
 270         private LogEvent(PlatformLogger.Level platformLevel,
 271                 String sourceClass, String sourceMethod,
 272                 ResourceBundle bundle, String msg,
 273                 Throwable thrown, Object[] params) {
 274             this.acc = AccessController.getContext();
 275             this.timeMillis = System.currentTimeMillis();
 276             this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
 277             this.level = null;
 278             this.platformLevel = platformLevel;
 279             this.bundle = bundle;
 280             this.msg = msg;
 281             this.msgSupplier = null;
 282             this.thrown = thrown;
 283             this.params = params;
 284             this.sourceClass = sourceClass;
 285             this.sourceMethod = sourceMethod;
 286         }
 287 
 288         private LogEvent(PlatformLogger.Level platformLevel,
 289                 String sourceClass, String sourceMethod,
 290                 Supplier<String> msgSupplier,
 291                 Throwable thrown, Object[] params) {
 292             this.acc = AccessController.getContext();
 293             this.timeMillis = System.currentTimeMillis();
 294             this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
 295             this.level = null;
 296             this.platformLevel = platformLevel;
 297             this.bundle = null;
 298             this.msg = null;
 299             this.msgSupplier = msgSupplier;
 300             this.thrown = thrown;
 301             this.params = params;
 302             this.sourceClass = sourceClass;
 303             this.sourceMethod = sourceMethod;
 304         }
 305 
 306         // Log this message in the given logger. Do not call directly.
 307         // Use LogEvent.log(LogEvent, logger) instead.
 308         private void log(Logger logger) {
 309             assert platformLevel == null && level != null;
 310             //new Exception("logging delayed message").printStackTrace();
 311             if (msgSupplier != null) {
 312                 if (thrown != null) {
 313                     logger.log(level, msgSupplier, thrown);
 314                 } else {
 315                     logger.log(level, msgSupplier);
 316                 }
 317             } else {
 318                 // BootstrapLoggers are never localized so we can safely
 319                 // use the method that takes a ResourceBundle parameter
 320                 // even when that resource bundle is null.
 321                 if (thrown != null) {
 322                     logger.log(level, bundle, msg, thrown);
 323                 } else {
 324                     logger.log(level, bundle, msg, params);
 325                 }
 326             }
 327         }
 328 
 329         // Log this message in the given logger.  Do not call directly.
 330         // Use LogEvent.doLog(LogEvent, logger) instead.
 331         private void log(PlatformLoggerBridge logger) {
 332             assert platformLevel != null && level == null;
 333             if (sourceClass == null) {
 334                 if (msgSupplier != null) {
 335                     if (thrown != null) {
 336                         logger.log(platformLevel, thrown, msgSupplier);
 337                     } else {
 338                         logger.log(platformLevel, msgSupplier);
 339                     }
 340                 } else {
 341                     // BootstrapLoggers are never localized so we can safely
 342                     // use the method that takes a ResourceBundle parameter
 343                     // even when that resource bundle is null.
 344                     if (thrown != null) {
 345                         logger.logrb(platformLevel, bundle, msg, thrown);
 346                     } else {
 347                         logger.logrb(platformLevel, bundle, msg, params);
 348                     }
 349                 }
 350             } else {
 351                 if (msgSupplier != null) {
 352                     if (thrown != null) {
 353                         logger.log(platformLevel, sourceClass, sourceMethod, thrown, msgSupplier);
 354                     } else {
 355                         logger.log(platformLevel,sourceClass, sourceMethod,  msgSupplier);
 356                     }
 357                 } else {
 358                     // BootstrapLoggers are never localized so we can safely
 359                     // use the method that takes a ResourceBundle parameter
 360                     // even when that resource bundle is null.
 361                     if (thrown != null) {
 362                         logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, thrown);
 363                     } else {
 364                         logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, params);
 365                     }
 366                 }
 367             }
 368         }
 369 
 370         // non default methods from Logger interface
 371         static LogEvent valueOf(Level level, ResourceBundle bundle, String key, Throwable thrown) {
 372             return new LogEvent(level, bundle, key, thrown, null);
 373         }
 374         static LogEvent valueOf(Level level, ResourceBundle bundle, String format, Object[] params) {
 375             return new LogEvent(level, bundle, format, null, params);
 376         }
 377         static LogEvent valueOf(Level level, Supplier<String> msgSupplier, Throwable thrown) {
 378             return new LogEvent(level, msgSupplier, thrown, null);
 379         }
 380         static LogEvent valueOf(Level level, Supplier<String> msgSupplier) {
 381             return new LogEvent(level, msgSupplier, null, null);
 382         }
 383         static void log(LogEvent log, Logger logger) {
 384             final SecurityManager sm = System.getSecurityManager();
 385             // not sure we can actually use lambda here. We may need to create
 386             // an anonymous class. Although if we reach here, then it means
 387             // the VM is booted.
 388             if (sm == null || log.acc == null) {
 389                 BootstrapExecutors.submit(() -> log.log(logger));
 390             } else {
 391                 BootstrapExecutors.submit(() ->
 392                     AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 393                         log.log(logger); return null;
 394                     }, log.acc));
 395             }
 396         }
 397 
 398         // non default methods from PlatformLoggerBridge interface
 399         static LogEvent valueOf(PlatformLogger.Level level, String msg) {
 400             return new LogEvent(level, null, null, null, msg, null, null);
 401         }
 402         static LogEvent valueOf(PlatformLogger.Level level, String msg, Throwable thrown) {
 403             return new LogEvent(level, null, null, null, msg, thrown, null);
 404         }
 405         static LogEvent valueOf(PlatformLogger.Level level, String msg, Object[] params) {
 406             return new LogEvent(level, null, null, null, msg, null, params);
 407         }
 408         static LogEvent valueOf(PlatformLogger.Level level, Supplier<String> msgSupplier) {
 409             return new LogEvent(level, null, null, msgSupplier, null, null);
 410         }
 411         static LogEvent vaueOf(PlatformLogger.Level level, Supplier<String> msgSupplier,
 412                 Throwable thrown) {
 413             return new LogEvent(level, null, null, msgSupplier, thrown, null);
 414         }
 415         static LogEvent valueOf(PlatformLogger.Level level, String sourceClass,
 416                 String sourceMethod, ResourceBundle bundle, String msg, Object[] params) {
 417             return new LogEvent(level, sourceClass, sourceMethod, bundle, msg, null, params);
 418         }
 419         static LogEvent valueOf(PlatformLogger.Level level, String sourceClass,
 420                 String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {
 421             return new LogEvent(level, sourceClass, sourceMethod, bundle, msg, thrown, null);
 422         }
 423         static LogEvent valueOf(PlatformLogger.Level level, String sourceClass,
 424                 String sourceMethod, Supplier<String> msgSupplier, Throwable thrown) {
 425             return new LogEvent(level, sourceClass, sourceMethod, msgSupplier, thrown, null);
 426         }
 427         static void log(LogEvent log, PlatformLoggerBridge logger) {
 428             final SecurityManager sm = System.getSecurityManager();
 429             if (sm == null || log.acc == null) {
 430                 log.log(logger);
 431             } else {
 432                 // not sure we can actually use lambda here. We may need to create
 433                 // an anonymous class. Although if we reach here, then it means
 434                 // the VM is booted.
 435                 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 436                     log.log(logger); return null;
 437                 }, log.acc);
 438             }
 439         }
 440 
 441     }
 442 
 443     // Push a log event at the end of the pending LogEvent queue.
 444     void push(LogEvent log) {
 445         synchronized(this) {
 446             if (head == null) {
 447                 head = tail = log;
 448             } else {
 449                 tail.next = log;
 450                 if (log != null) tail=log;
 451             }
 452         }
 453         // if the queue has been flushed just before we entered
 454         // the synchronized block we need to flush it again.
 455         checkBootstrapping();
 456     }
 457 
 458     // Flushes the queue of pending LogEvents to the logger.
 459     void flush(Logger logger) {
 460         LogEvent first;
 461         synchronized (this) {
 462             first = head;
 463             head = tail = null;
 464         }
 465         PlatformLoggerBridge platform = null;
 466         while (first != null) {
 467             LogEvent next = first.next;
 468             first.next = null;
 469             if (first.platformLevel != null) {
 470                 if (platform == null) {
 471                     platform = PlatformLoggerBridge.convert(logger);
 472                 }
 473                 LogEvent.log(first, platform);
 474             } else {
 475                 LogEvent.log(first, logger);
 476             }
 477             first = next;
 478         }
 479     }
 480 
 481     /**
 482      * The name of this logger. This is the name of the actual logger for which
 483      * this logger acts as a temporary proxy.
 484      * @return The logger name.
 485      */
 486     @Override
 487     public String getName() {
 488         return holder.name;
 489     }
 490 
 491     /**
 492      * Check whether the VM is still bootstrapping, and if not, arranges
 493      * for this logger's holder to create the real logger and flush the
 494      * pending event queue.
 495      * @return true if the VM is still bootstrapping.
 496      */
 497     boolean checkBootstrapping() {
 498         if (isBooted()) {
 499             holder.flush(this);
 500             return false;
 501         }
 502         return true;
 503     }
 504 
 505     // ----------------------------------
 506     // Methods from Logger
 507     // ----------------------------------
 508 
 509     @Override
 510     public boolean isLoggable(Level level) {
 511         if (checkBootstrapping()) {
 512             return level.getSeverity() >= Level.INFO.getSeverity();
 513         } else {
 514             final Logger spi = holder.wrapped();
 515             return spi.isLoggable(level);
 516         }
 517     }
 518 
 519     @Override
 520     public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
 521         if (checkBootstrapping()) {
 522             push(LogEvent.valueOf(level, bundle, key, thrown));
 523         } else {
 524             final Logger spi = holder.wrapped();
 525             spi.log(level, bundle, key, thrown);
 526         }
 527     }
 528 
 529     @Override
 530     public void log(Level level, ResourceBundle bundle, String format, Object... params) {
 531         if (checkBootstrapping()) {
 532             push(LogEvent.valueOf(level, bundle, format, params));
 533         } else {
 534             final Logger spi = holder.wrapped();
 535             spi.log(level, bundle, format, params);
 536         }
 537     }
 538 
 539     @Override
 540     public void log(Level level, String msg, Throwable thrown) {
 541         if (checkBootstrapping()) {
 542             push(LogEvent.valueOf(level, null, msg, thrown));
 543         } else {
 544             final Logger spi = holder.wrapped();
 545             spi.log(level, msg, thrown);
 546         }
 547     }
 548 
 549     @Override
 550     public void log(Level level, String format, Object... params) {
 551         if (checkBootstrapping()) {
 552             push(LogEvent.valueOf(level, null, format, params));
 553         } else {
 554             final Logger spi = holder.wrapped();
 555             spi.log(level, format, params);
 556         }
 557     }
 558 
 559     @Override
 560     public void log(Level level, Supplier<String> msgSupplier) {
 561         if (checkBootstrapping()) {
 562             push(LogEvent.valueOf(level, msgSupplier));
 563         } else {
 564             final Logger spi = holder.wrapped();
 565             spi.log(level, msgSupplier);
 566         }
 567     }
 568 
 569     @Override
 570     public void log(Level level, Object obj) {
 571         if (checkBootstrapping()) {
 572             Logger.super.log(level, obj);
 573         } else {
 574             final Logger spi = holder.wrapped();
 575             spi.log(level, obj);
 576         }
 577     }
 578 
 579     @Override
 580     public void log(Level level, String msg) {
 581         if (checkBootstrapping()) {
 582             push(LogEvent.valueOf(level, null, msg, (Object[])null));
 583         } else {
 584             final Logger spi = holder.wrapped();
 585             spi.log(level, msg);
 586         }
 587     }
 588 
 589     @Override
 590     public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
 591         if (checkBootstrapping()) {
 592             push(LogEvent.valueOf(level, msgSupplier, thrown));
 593         } else {
 594             final Logger spi = holder.wrapped();
 595             spi.log(level, msgSupplier, thrown);
 596         }
 597     }
 598 
 599     // ----------------------------------
 600     // Methods from PlatformLoggerBridge
 601     // ----------------------------------
 602 
 603     @Override
 604     public boolean isLoggable(PlatformLogger.Level level) {
 605         if (checkBootstrapping()) {
 606             return level.intValue() >= PlatformLogger.Level.INFO.intValue();
 607         } else {
 608             final PlatformLoggerBridge spi = holder.platform();
 609             return spi.isLoggable(level);
 610         }
 611     }
 612 
 613     @Override
 614     public boolean isEnabled() {
 615         if (checkBootstrapping()) {
 616             return true;
 617         } else {
 618             final PlatformLoggerBridge spi = holder.platform();
 619             return spi.isEnabled();
 620         }
 621     }
 622 
 623     @Override
 624     public void log(PlatformLogger.Level level, String msg) {
 625         if (checkBootstrapping()) {
 626             push(LogEvent.valueOf(level, msg));
 627         } else {
 628             final PlatformLoggerBridge spi = holder.platform();
 629             spi.log(level, msg);
 630         }
 631     }
 632 
 633     @Override
 634     public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
 635         if (checkBootstrapping()) {
 636             push(LogEvent.valueOf(level, msg, thrown));
 637         } else {
 638             final PlatformLoggerBridge spi = holder.platform();
 639             spi.log(level, msg, thrown);
 640         }
 641     }
 642 
 643     @Override
 644     public void log(PlatformLogger.Level level, String msg, Object... params) {
 645         if (checkBootstrapping()) {
 646             push(LogEvent.valueOf(level, msg, params));
 647         } else {
 648             final PlatformLoggerBridge spi = holder.platform();
 649             spi.log(level, msg, params);
 650         }
 651     }
 652 
 653     @Override
 654     public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
 655         if (checkBootstrapping()) {
 656             push(LogEvent.valueOf(level, msgSupplier));
 657         } else {
 658             final PlatformLoggerBridge spi = holder.platform();
 659             spi.log(level, msgSupplier);
 660         }
 661     }
 662 
 663     @Override
 664     public void log(PlatformLogger.Level level, Throwable thrown,
 665             Supplier<String> msgSupplier) {
 666         if (checkBootstrapping()) {
 667             push(LogEvent.vaueOf(level, msgSupplier, thrown));
 668         } else {
 669             final PlatformLoggerBridge spi = holder.platform();
 670             spi.log(level, thrown, msgSupplier);
 671         }
 672     }
 673 
 674     @Override
 675     public void logp(PlatformLogger.Level level, String sourceClass,
 676             String sourceMethod, String msg) {
 677         if (checkBootstrapping()) {
 678             push(LogEvent.valueOf(level, sourceClass, sourceMethod, null,
 679                     msg, (Object[])null));
 680         } else {
 681             final PlatformLoggerBridge spi = holder.platform();
 682             spi.logp(level, sourceClass, sourceMethod, msg);
 683         }
 684     }
 685 
 686     @Override
 687     public void logp(PlatformLogger.Level level, String sourceClass,
 688             String sourceMethod, Supplier<String> msgSupplier) {
 689         if (checkBootstrapping()) {
 690             push(LogEvent.valueOf(level, sourceClass, sourceMethod, msgSupplier, null));
 691         } else {
 692             final PlatformLoggerBridge spi = holder.platform();
 693             spi.logp(level, sourceClass, sourceMethod, msgSupplier);
 694         }
 695     }
 696 
 697     @Override
 698     public void logp(PlatformLogger.Level level, String sourceClass,
 699             String sourceMethod, String msg, Object... params) {
 700         if (checkBootstrapping()) {
 701             push(LogEvent.valueOf(level, sourceClass, sourceMethod, null, msg, params));
 702         } else {
 703             final PlatformLoggerBridge spi = holder.platform();
 704             spi.logp(level, sourceClass, sourceMethod, msg, params);
 705         }
 706     }
 707 
 708     @Override
 709     public void logp(PlatformLogger.Level level, String sourceClass,
 710             String sourceMethod, String msg, Throwable thrown) {
 711         if (checkBootstrapping()) {
 712             push(LogEvent.valueOf(level, sourceClass, sourceMethod, null, msg, thrown));
 713         } else {
 714             final PlatformLoggerBridge spi = holder.platform();
 715             spi.logp(level, sourceClass, sourceMethod, msg, thrown);
 716         }
 717     }
 718 
 719     @Override
 720     public void logp(PlatformLogger.Level level, String sourceClass,
 721             String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {
 722         if (checkBootstrapping()) {
 723             push(LogEvent.valueOf(level, sourceClass, sourceMethod, msgSupplier, thrown));
 724         } else {
 725             final PlatformLoggerBridge spi = holder.platform();
 726             spi.logp(level, sourceClass, sourceMethod, thrown, msgSupplier);
 727         }
 728     }
 729 
 730     @Override
 731     public void logrb(PlatformLogger.Level level, String sourceClass,
 732             String sourceMethod, ResourceBundle bundle, String msg, Object... params) {
 733         if (checkBootstrapping()) {
 734             push(LogEvent.valueOf(level, sourceClass, sourceMethod, bundle, msg, params));
 735         } else {
 736             final PlatformLoggerBridge spi = holder.platform();
 737             spi.logrb(level, sourceClass, sourceMethod, bundle, msg, params);
 738         }
 739     }
 740 
 741     @Override
 742     public void logrb(PlatformLogger.Level level, String sourceClass,
 743             String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {
 744         if (checkBootstrapping()) {
 745             push(LogEvent.valueOf(level, sourceClass, sourceMethod, bundle, msg, thrown));
 746         } else {
 747             final PlatformLoggerBridge spi = holder.platform();
 748             spi.logrb(level, sourceClass, sourceMethod, bundle, msg, thrown);
 749         }
 750     }
 751 
 752     @Override
 753     public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
 754             String msg, Object... params) {
 755         if (checkBootstrapping()) {
 756             push(LogEvent.valueOf(level, null, null, bundle, msg, params));
 757         } else {
 758             final PlatformLoggerBridge spi = holder.platform();
 759             spi.logrb(level, bundle, msg, params);
 760         }
 761     }
 762 
 763     @Override
 764     public void logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Throwable thrown) {
 765         if (checkBootstrapping()) {
 766             push(LogEvent.valueOf(level, null, null, bundle, msg, thrown));
 767         } else {
 768             final PlatformLoggerBridge spi = holder.platform();
 769             spi.logrb(level, bundle, msg, thrown);
 770         }
 771     }
 772 
 773     @Override
 774     public LoggerConfiguration getLoggerConfiguration() {
 775         if (checkBootstrapping()) {
 776             // This practically means that PlatformLogger.setLevel()
 777             // calls will be ignored if the VM is still bootstrapping. We could
 778             // attempt to fix that but is it worth it?
 779             return ConfigurableLoggerBridge.super.getLoggerConfiguration();
 780         } else {
 781             final PlatformLoggerBridge spi = holder.platform();
 782             return ConfigurableLoggerBridge.getLoggerConfiguration(spi);
 783         }
 784     }
 785 
 786     // This BooleanSupplier is a hook for tests - so that we can simulate
 787     // what would happen before the VM is booted.
 788     private static volatile BooleanSupplier isBooted;
 789     public static boolean isBooted() {
 790         if (isBooted != null) return isBooted.getAsBoolean();
 791         else return VM.isBooted();
 792     }
 793 
 794     // A bit of black magic. We try to find out the nature of the logging
 795     // backend without actually loading it.
 796     private static enum LoggingBackend {
 797         // There is no LoggerFinder and JUL is not present
 798         NONE(true),
 799 
 800         // There is no LoggerFinder, but we have found a
 801         // JdkLoggerFinder installed (which means JUL is present),
 802         // and we haven't found any custom configuration for JUL.
 803         // Until LogManager is initialized we can use a simple console
 804         // logger.
 805         JUL_DEFAULT(false),
 806 
 807         // Same as above, except that we have found a custom configuration
 808         // for JUL. We cannot use the simple console logger in this case.
 809         JUL_WITH_CONFIG(true),
 810 
 811         // We have found a custom LoggerFinder.
 812         CUSTOM(true);
 813 
 814         final boolean useLoggerFinder;
 815         private LoggingBackend(boolean useLoggerFinder) {
 816             this.useLoggerFinder = useLoggerFinder;
 817         }
 818     };
 819 
 820     // The purpose of this class is to delay the initialization of
 821     // the detectedBackend field until it is actually read.
 822     // We do not want this field to get initialized if VM.isBooted() is false.
 823     private static final class DetectBackend {
 824         static final LoggingBackend detectedBackend;
 825         static {
 826             detectedBackend = AccessController.doPrivileged(new PrivilegedAction<LoggingBackend>() {
 827                     @Override
 828                     public LoggingBackend run() {
 829                         final Iterator<LoggerFinder> iterator =
 830                             ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader())
 831                             .iterator();
 832                         if (iterator.hasNext()) {
 833                             return LoggingBackend.CUSTOM; // Custom Logger Provider is registered
 834                         }
 835                         // No custom logger provider: we will be using the default
 836                         // backend.
 837                         final Iterator<JdkLoggerProvider> iterator2 =
 838                             ServiceLoader.loadInstalled(JdkLoggerProvider.class)
 839                             .iterator();
 840                         if (iterator2.hasNext()) {
 841                             // JdkLoggingProvider is registered. The default
 842                             // implementation is java.util.logging
 843                             String cname = System.getProperty("java.util.logging.config.class");
 844                             String fname = System.getProperty("java.util.logging.config.file");
 845                             return (cname != null || fname != null)
 846                                 ? LoggingBackend.JUL_WITH_CONFIG
 847                                 : LoggingBackend.JUL_DEFAULT;
 848                         } else {
 849                             // SimpleLogger is used
 850                             return LoggingBackend.NONE;
 851                         }
 852                     }
 853                 });
 854             // System.out.println("Detected backend: "+detectedBackend);
 855 
 856             // Not sure whether this is needed: it was in the original
 857             //     PlatformLogger proxy code:
 858             //
 859             // force loading of all JavaLoggerProxy (sub)classes to make JIT de-optimizations
 860             // less probable.  Don't initialize JavaLoggerProxy class since
 861             // java.util.logging may not be enabled.
 862             try {
 863                 Class.forName("sun.util.logger.LazyLoggers$JdkLazyLogger",
 864                           false,
 865                           BootstrapLogger.class.getClassLoader());
 866             } catch (ClassNotFoundException ex) {
 867                 throw new InternalError(ex);
 868             }
 869         }
 870     }
 871 
 872     // We will use temporary SimpleConsoleLoggers if
 873     // the logging backend is JUL, there is no custom config,
 874     // and the LogManager has not been initialized yet.
 875     private static  boolean useTemporaryLoggers() {
 876         // being paranoid: this should already have been checked
 877         if (!isBooted()) return true;
 878         return DetectBackend.detectedBackend == LoggingBackend.JUL_DEFAULT
 879                 && !logManagerConfigured;
 880     }
 881 
 882     // We will use lazy loggers if:
 883     //    - the VM is not yet booted
 884     //    - the logging backend is a custom backend
 885     //    - the logging backend is JUL, there is no custom config,
 886     //      and the LogManager has not been initialized yet.
 887     public static synchronized boolean useLazyLoggers() {
 888         return !BootstrapLogger.isBooted()
 889                 || DetectBackend.detectedBackend == LoggingBackend.CUSTOM
 890                 || useTemporaryLoggers();
 891     }
 892 
 893     // Called by LazyLoggerAccessor. This method will determine whether
 894     // to create a BootstrapLogger (if the VM is not yet booted),
 895     // a SimpleConsoleLogger (if JUL is the default backend and there
 896     // is no custom JUL configuration and LogManager is not yet initialized),
 897     // or a logger returned by the loaded LoggerFinder (all other cases).
 898     static Logger getLogger(LazyLoggerAccessor accessor) {
 899         if (!BootstrapLogger.isBooted()) {
 900             return new BootstrapLogger(accessor);
 901         } else {
 902             boolean temporary = useTemporaryLoggers();
 903             if (temporary) {
 904                 // JUL is the default backend, there is no custom configuration,
 905                 // LogManager has not been used.
 906                 synchronized(BootstrapLogger.class) {
 907                     if (useTemporaryLoggers()) {
 908                         return makeTemporaryLogger(accessor);
 909                     }
 910                 }
 911             }
 912             // Already booted. Return the real logger.
 913             return accessor.createLogger();
 914         }
 915     }
 916 
 917 
 918     // If the backend is JUL, and there is no custom configuration, and
 919     // nobody has attempted to call LogManager.getLogManager() yet, then
 920     // we can temporarily substitute JUL Logger with SimpleConsoleLoggers,
 921     // which avoids the cost of actually loading up the LogManager...
 922     // The TemporaryLoggers class has the logic to create such temporary
 923     // loggers, and to possibly replace them with real JUL loggers if
 924     // someone calls LogManager.getLogManager().
 925     static final class TemporaryLoggers implements
 926             Function<LazyLoggerAccessor, SimpleConsoleLogger> {
 927 
 928         // all accesses must be synchronized on the outer BootstrapLogger.class
 929         final Map<LazyLoggerAccessor, SimpleConsoleLogger> temporaryLoggers =
 930                 new HashMap<>();
 931 
 932         // all accesses must be synchronized on the outer BootstrapLogger.class
 933         // The temporaryLoggers map will be cleared when LogManager is initialized.
 934         boolean cleared;
 935 
 936         @Override
 937         // all accesses must be synchronized on the outer BootstrapLogger.class
 938         public SimpleConsoleLogger apply(LazyLoggerAccessor t) {
 939             if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
 940             return SimpleConsoleLogger.makeSimpleLogger(t.getLoggerName(), true);
 941         }
 942 
 943         // all accesses must be synchronized on the outer BootstrapLogger.class
 944         SimpleConsoleLogger get(LazyLoggerAccessor a) {
 945             if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
 946             return temporaryLoggers.computeIfAbsent(a, this);
 947         }
 948 
 949         // all accesses must be synchronized on the outer BootstrapLogger.class
 950         Map<LazyLoggerAccessor, SimpleConsoleLogger> drainTemporaryLoggers() {
 951             if (temporaryLoggers.isEmpty()) return null;
 952             if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
 953             final Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors = new HashMap<>(temporaryLoggers);
 954             temporaryLoggers.clear();
 955             cleared = true;
 956             return accessors;
 957         }
 958 
 959         static void resetTemporaryLoggers(Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors) {
 960             // When the backend is JUL we want to force the creation of
 961             // JUL loggers here: some tests are expecting that the
 962             // PlatformLogger will create JUL loggers as soon as the
 963             // LogManager is initialized.
 964             //
 965             // If the backend is not JUL then we can delay the re-creation
 966             // of the wrapped logger until they are next accessed.
 967             //
 968             final LoggingBackend detectedBackend = DetectBackend.detectedBackend;
 969             final boolean lazy = detectedBackend != LoggingBackend.JUL_DEFAULT
 970                     && detectedBackend != LoggingBackend.JUL_WITH_CONFIG;
 971             for (Map.Entry<LazyLoggerAccessor, SimpleConsoleLogger> a : accessors.entrySet()) {
 972                 a.getKey().release(a.getValue(), !lazy);
 973             }
 974         }
 975 
 976         // all accesses must be synchronized on the outer BootstrapLogger.class
 977         static final TemporaryLoggers INSTANCE = new TemporaryLoggers();
 978     }
 979 
 980     static synchronized Logger makeTemporaryLogger(LazyLoggerAccessor a) {
 981         // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
 982         return TemporaryLoggers.INSTANCE.get(a);
 983     }
 984 
 985     private static volatile boolean logManagerConfigured;
 986 
 987     private static synchronized Map<LazyLoggerAccessor, SimpleConsoleLogger>
 988          releaseTemporaryLoggers() {
 989         // first check whether there's a chance that we have used
 990         // temporary loggers; Will be false if logManagerConfigured is already
 991         // true.
 992         final boolean clearTemporaryLoggers = useTemporaryLoggers();
 993 
 994         // then sets the flag that tells that the log manager is configured
 995         logManagerConfigured = true;
 996 
 997         // finally replace all temporary loggers by real JUL loggers
 998         if (clearTemporaryLoggers) {
 999             // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
1000             return TemporaryLoggers.INSTANCE.drainTemporaryLoggers();
1001         } else {
1002             return null;
1003         }
1004     }
1005 
1006     public static void redirectTemporaryLoggers() {
1007         // This call is synchronized on BootstrapLogger.class.
1008         final Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors =
1009                 releaseTemporaryLoggers();
1010 
1011         // We will now reset the logger accessors, triggering the
1012         // (possibly lazy) replacement of any temporary logger by the
1013         // real logger returned from the loaded LoggerFinder.
1014         if (accessors != null) {
1015             TemporaryLoggers.resetTemporaryLoggers(accessors);
1016         }
1017     }
1018 
1019     // Hook for tests which need to wait until pending messages
1020     // are processed.
1021     static void awaitPendingTasks() {
1022         BootstrapExecutors.awaitPendingTasks();
1023     }
1024     static boolean isAlive() {
1025         return BootstrapExecutors.isAlive();
1026     }
1027 
1028 }