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 }