1 /*
   2  * Copyright (c) 2015, 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 jdk.internal.logger;
  27 
  28 import java.security.AccessController;
  29 import java.security.PrivilegedAction;
  30 import java.util.function.BiFunction;
  31 import java.lang.System.LoggerFinder;
  32 import java.lang.System.Logger;
  33 import java.lang.ref.WeakReference;
  34 import java.lang.reflect.Module;
  35 import java.util.Objects;
  36 import jdk.internal.misc.VM;
  37 import sun.util.logging.PlatformLogger;
  38 
  39 /**
  40  * This class is a factory for Lazy Loggers; only system loggers can be
  41  * Lazy Loggers.
  42  */
  43 public final class LazyLoggers {
  44 
  45     static final RuntimePermission LOGGERFINDER_PERMISSION =
  46                 new RuntimePermission("loggerFinder");
  47 
  48     private LazyLoggers() {
  49         throw new InternalError();
  50     }
  51 
  52     /**
  53      * This class is used to hold the factories that a Lazy Logger will use
  54      * to create (or map) its wrapped logger.
  55      * @param <L> {@link Logger} or a subclass of {@link Logger}.
  56      */
  57     private static final class LazyLoggerFactories<L extends Logger> {
  58 
  59         /**
  60          * A factory method to create an SPI logger.
  61          * Usually, this will be something like LazyLoggers::getSystemLogger.
  62          */
  63         final BiFunction<String, Module, L> loggerSupplier;
  64 
  65 
  66         public LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier) {
  67             this(Objects.requireNonNull(loggerSupplier),
  68                  (Void)null);
  69         }
  70 
  71         private LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier,
  72                           Void unused) {
  73             this.loggerSupplier = loggerSupplier;
  74         }
  75 
  76     }
  77 
  78     static interface LoggerAccessor {
  79         /**
  80          * The logger name.
  81          * @return The name of the logger that is / will be lazily created.
  82          */
  83         public String getLoggerName();
  84 
  85         /**
  86          * Returns the wrapped logger object.
  87          * @return the wrapped logger object.
  88          */
  89         public Logger wrapped();
  90 
  91         /**
  92          * A PlatformLogger.Bridge view of the wrapped logger object.
  93          * @return A PlatformLogger.Bridge view of the wrapped logger object.
  94          */
  95         public PlatformLogger.Bridge platform();
  96     }
  97 
  98     /**
  99      * The LazyLoggerAccessor class holds all the logic that delays the creation
 100      * of the SPI logger until such a time that the VM is booted and the logger
 101      * is actually used for logging.
 102      *
 103      * This class uses the services of the BootstrapLogger class to instantiate
 104      * temporary loggers if appropriate.
 105      */
 106     static final class LazyLoggerAccessor implements LoggerAccessor {
 107 
 108         // The factories that will be used to create the logger lazyly
 109         final LazyLoggerFactories<? extends Logger> factories;
 110 
 111         // We need to pass the actual caller module when creating the logger.
 112         private final WeakReference<Module> moduleRef;
 113 
 114         // The name of the logger that will be created lazyly
 115         final String name;
 116         // The plain logger SPI object - null until it is accessed for the
 117         // first time.
 118         private volatile Logger w;
 119         // A PlatformLogger.Bridge view of w.
 120         private volatile PlatformLogger.Bridge p;
 121 
 122 
 123         private LazyLoggerAccessor(String name,
 124                                    LazyLoggerFactories<? extends Logger> factories,
 125                                    Module module) {
 126             this(Objects.requireNonNull(name), Objects.requireNonNull(factories),
 127                     Objects.requireNonNull(module), null);
 128         }
 129 
 130         private LazyLoggerAccessor(String name,
 131                                    LazyLoggerFactories<? extends Logger> factories,
 132                                    Module module, Void unused) {
 133             this.name = name;
 134             this.factories = factories;
 135             this.moduleRef = new WeakReference<>(module);
 136         }
 137 
 138         /**
 139          * The logger name.
 140          * @return The name of the logger that is / will be lazily created.
 141          */
 142         @Override
 143         public String getLoggerName() {
 144             return name;
 145         }
 146 
 147         // must be called in synchronized block
 148         // set wrapped logger if not set
 149         private void setWrappedIfNotSet(Logger wrapped) {
 150             if (w == null) {
 151                 w = wrapped;
 152             }
 153         }
 154 
 155         /**
 156          * Returns the logger SPI object, creating it if 'w' is still null.
 157          * @return the logger SPI object.
 158          */
 159         public Logger wrapped() {
 160             Logger wrapped = w;
 161             if (wrapped != null) return wrapped;
 162             // Wrapped logger not created yet: create it.
 163             // BootstrapLogger has the logic to decide whether to invoke the
 164             // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
 165             // logger.
 166             wrapped = BootstrapLogger.getLogger(this);
 167             synchronized(this) {
 168                 // if w has already been in between, simply drop 'wrapped'.
 169                 setWrappedIfNotSet(wrapped);
 170                 return w;
 171             }
 172         }
 173 
 174         /**
 175          * A PlatformLogger.Bridge view of the wrapped logger.
 176          * @return A PlatformLogger.Bridge view of the wrapped logger.
 177          */
 178         public PlatformLogger.Bridge platform() {
 179             // We can afford to return the platform view of the previous
 180             // logger - if that view is not null.
 181             // Because that view will either be the BootstrapLogger, which
 182             // will redirect to the new wrapper properly, or the temporary
 183             // logger - which in effect is equivalent to logging something
 184             // just before the application initialized LogManager.
 185             PlatformLogger.Bridge platform = p;
 186             if (platform != null) return platform;
 187             synchronized (this) {
 188                 if (w != null) {
 189                     if (p == null) p = PlatformLogger.Bridge.convert(w);
 190                     return p;
 191                 }
 192             }
 193             // If we reach here it means that the wrapped logger may not
 194             // have been created yet: attempt to create it.
 195             // BootstrapLogger has the logic to decide whether to invoke the
 196             // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
 197             // logger.
 198             final Logger wrapped = BootstrapLogger.getLogger(this);
 199             synchronized(this) {
 200                 // if w has already been set, simply drop 'wrapped'.
 201                 setWrappedIfNotSet(wrapped);
 202                 if (p == null) p = PlatformLogger.Bridge.convert(w);
 203                 return p;
 204             }
 205         }
 206 
 207         /**
 208          * Makes this accessor release a temporary logger.
 209          * This method is called
 210          * by BootstrapLogger when JUL is the default backend and LogManager
 211          * is initialized, in order to replace temporary SimpleConsoleLoggers by
 212          * real JUL loggers. See BootstrapLogger for more details.
 213          * If {@code replace} is {@code true}, then this method will force
 214          * the accessor to eagerly recreate its wrapped logger.
 215          * Note: passing {@code replace=false} is no guarantee that the
 216          * method will not actually replace the released logger.
 217          * @param temporary The temporary logger too be released.
 218          * @param replace   Whether the released logger should be eagerly
 219          *                  replaced.
 220          */
 221         void release(SimpleConsoleLogger temporary, boolean replace) {
 222             PlatformLogger.ConfigurableBridge.LoggerConfiguration conf =
 223                 PlatformLogger.ConfigurableBridge.getLoggerConfiguration(temporary);
 224             PlatformLogger.Level level = conf != null
 225                     ? conf.getPlatformLevel()
 226                     : null;
 227             synchronized (this) {
 228                 if (this.w == temporary) {
 229                     this.w = null; this.p = null;
 230                 }
 231             }
 232             PlatformLogger.Bridge platform =  replace || level != null
 233                     ? this.platform() : null;
 234 
 235             if (level != null) {
 236                 conf = (platform != null && platform != temporary)
 237                         ? PlatformLogger.ConfigurableBridge.getLoggerConfiguration(platform)
 238                         : null;
 239                 if (conf != null) conf.setPlatformLevel(level);
 240             }
 241         }
 242 
 243         /**
 244          * Replace 'w' by the real SPI logger and flush the log messages pending
 245          * in the temporary 'bootstrap' Logger. Called by BootstrapLogger when
 246          * this accessor's bootstrap logger is accessed and BootstrapLogger
 247          * notices that the VM is no longer booting.
 248          * @param bootstrap This accessor's bootstrap logger (usually this is 'w').
 249          */
 250         Logger getConcreteLogger(BootstrapLogger bootstrap) {
 251             assert VM.isBooted();
 252             synchronized(this) {
 253                 // another thread may have already invoked flush()
 254                 if (this.w == bootstrap) {
 255                     this.w = null; this.p = null;
 256                 }
 257             }
 258             return this.wrapped();
 259         }
 260 
 261         PlatformLogger.Bridge getConcretePlatformLogger(BootstrapLogger bootstrap) {
 262             assert VM.isBooted();
 263             synchronized(this) {
 264                 // another thread may have already invoked flush()
 265                 if (this.w == bootstrap) {
 266                     this.w = null; this.p = null;
 267                 }
 268             }
 269             return this.platform();
 270         }
 271 
 272         // Creates the wrapped logger by invoking the SPI.
 273         Logger createLogger() {
 274             final Module module = moduleRef.get();
 275             if (module == null) {
 276                 throw new IllegalStateException("The module for which this logger"
 277                         + " was created has been garbage collected");
 278             }
 279             return this.factories.loggerSupplier.apply(name, module);
 280         }
 281 
 282         /**
 283          * Creates a new lazy logger accessor for the named logger. The given
 284          * factories will be use when it becomes necessary to actually create
 285          * the logger.
 286          * @param <T> An interface that extends {@link Logger}.
 287          * @param name The logger name.
 288          * @param factories The factories that should be used to create the
 289          *                  wrapped logger.
 290          * @return A new LazyLoggerAccessor.
 291          */
 292         public static LazyLoggerAccessor makeAccessor(String name,
 293                 LazyLoggerFactories<? extends Logger> factories, Module module) {
 294                 return new LazyLoggerAccessor(name, factories, module);
 295         }
 296 
 297     }
 298 
 299     /**
 300      * An implementation of {@link Logger} that redirects all calls to a wrapped
 301      * instance of {@code Logger}.
 302      */
 303     private static class LazyLoggerWrapper
 304         extends AbstractLoggerWrapper<Logger> {
 305 
 306         final LoggerAccessor loggerAccessor;
 307 
 308         public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) {
 309             this(Objects.requireNonNull(loggerSinkSupplier), (Void)null);
 310         }
 311 
 312         private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier,
 313                 Void unused) {
 314             this.loggerAccessor = loggerSinkSupplier;
 315         }
 316 
 317         @Override
 318         final Logger wrapped() {
 319             return loggerAccessor.wrapped();
 320         }
 321 
 322         @Override
 323         PlatformLogger.Bridge platformProxy() {
 324             return loggerAccessor.platform();
 325         }
 326 
 327     }
 328 
 329     // Do not expose this outside of this package.
 330     private static volatile LoggerFinder provider;
 331     private static LoggerFinder accessLoggerFinder() {
 332         LoggerFinder prov = provider;
 333         if (prov == null) {
 334             // no need to lock: it doesn't matter if we call
 335             // getLoggerFinder() twice - since LoggerFinder already caches
 336             // the result.
 337             // This is just an optimization to avoid the cost of calling
 338             // doPrivileged every time.
 339             final SecurityManager sm = System.getSecurityManager();
 340             prov = sm == null ? LoggerFinder.getLoggerFinder() :
 341                 AccessController.doPrivileged(
 342                         (PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder);
 343             provider = prov;
 344         }
 345         return prov;
 346     }
 347 
 348     // Avoid using lambda here as lazy loggers could be created early
 349     // in the bootstrap sequence...
 350     private static final BiFunction<String, Module, Logger> loggerSupplier =
 351            new BiFunction<>() {
 352         @Override
 353         public Logger apply(String name, Module module) {
 354             return LazyLoggers.getLoggerFromFinder(name, module);
 355         }
 356     };
 357 
 358     private static final LazyLoggerFactories<Logger> factories =
 359            new LazyLoggerFactories<>(loggerSupplier);
 360 
 361 
 362 
 363     // A concrete implementation of Logger that delegates to a  System.Logger,
 364     // but only creates the System.Logger instance lazily when it's used for
 365     // the first time.
 366     // The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies
 367     // on the logic embedded in BootstrapLogger to avoid loading the concrete
 368     // logger provider until the VM has finished booting.
 369     //
 370     private static final class JdkLazyLogger extends LazyLoggerWrapper {
 371         JdkLazyLogger(String name, Module module) {
 372             this(LazyLoggerAccessor.makeAccessor(name, factories, module),
 373                  (Void)null);
 374         }
 375         private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) {
 376             super(holder);
 377         }
 378     }
 379 
 380     /**
 381      * Gets a logger from the LoggerFinder. Creates the actual concrete
 382      * logger.
 383      * @param name    name of the logger
 384      * @param module  module on behalf of which the logger is created
 385      * @return  The logger returned by the LoggerFinder.
 386      */
 387     static Logger getLoggerFromFinder(String name, Module module) {
 388         final SecurityManager sm = System.getSecurityManager();
 389         if (sm == null) {
 390             return accessLoggerFinder().getLogger(name, module);
 391         } else {
 392             return AccessController.doPrivileged((PrivilegedAction<Logger>)
 393                     () -> {return accessLoggerFinder().getLogger(name, module);},
 394                     null, LOGGERFINDER_PERMISSION);
 395         }
 396     }
 397 
 398     /**
 399      * Returns a (possibly lazy) Logger for the caller.
 400      *
 401      * @param name the logger name
 402      * @param module The module on behalf of which the logger is created.
 403      *               If the module is not loaded from the Boot ClassLoader,
 404      *               the LoggerFinder is accessed and the logger returned
 405      *               by {@link LoggerFinder#getLogger(java.lang.String, java.lang.reflect.Module)}
 406      *               is returned to the caller directly.
 407      *               Otherwise, the logger returned by
 408      *               {@link #getLazyLogger(java.lang.String, java.lang.reflect.Module)}
 409      *               is returned to the caller.
 410      *
 411      * @return  a (possibly lazy) Logger instance.
 412      */
 413     public static final Logger getLogger(String name, Module module) {
 414         if (DefaultLoggerFinder.isSystem(module)) {
 415             return getLazyLogger(name, module);
 416         } else {
 417             return getLoggerFromFinder(name, module);
 418         }
 419     }
 420 
 421     /**
 422      * Returns a (possibly lazy) Logger suitable for system classes.
 423      * Whether the returned logger is lazy or not depend on the result
 424      * returned by {@link BootstrapLogger#useLazyLoggers()}.
 425      *
 426      * @param name the logger name
 427      * @param module the module on behalf of which the logger is created.
 428      * @return  a (possibly lazy) Logger instance.
 429      */
 430     public static final Logger getLazyLogger(String name, Module module) {
 431 
 432         // BootstrapLogger has the logic to determine whether a LazyLogger
 433         // should be used. Usually, it is worth it only if:
 434         //   - the VM is not yet booted
 435         //   - or, the backend is JUL and there is no configuration
 436         //   - or, the backend is a custom backend, as we don't know what
 437         //     that is going to load...
 438         // So if for instance the VM is booted and we use JUL with a custom
 439         // configuration, we're not going to delay the creation of loggers...
 440         final boolean useLazyLogger = BootstrapLogger.useLazyLoggers();
 441         if (useLazyLogger) {
 442             return new JdkLazyLogger(name, module);
 443         } else {
 444             // Directly invoke the LoggerFinder.
 445             return getLoggerFromFinder(name, module);
 446         }
 447     }
 448 
 449 }