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