--- /dev/null 2015-10-09 21:42:02.000000000 +0200 +++ new/jdk/src/java.base/share/classes/sun/util/logger/LazyLoggers.java 2015-10-09 21:42:02.000000000 +0200 @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.util.logger; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.function.BiFunction; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.ref.WeakReference; +import java.security.AccessControlContext; +import java.util.Objects; +import sun.util.logging.ConfigurableLoggerBridge; +import sun.util.logging.PlatformLogger; +import sun.util.logging.PlatformLoggerBridge; + +/** + * This class is a factory for Lazy Loggers; only system loggers can be + * Lazy Loggers. + * + */ +public final class LazyLoggers { + + private LazyLoggers() { + throw new InternalError(); + } + + /** + * This class is used to hold the factories that a Lazy Logger will use + * to create (or map) its wrapped logger. + * @param {@link Logger} or a subclass of {@link Logger}. + * @since 9 + */ + private static final class LazyLoggerFactories { + + /** + * A factory method to create an SPI logger. + * Usually, this will be something like LazyLoggers::getSystemLogger. + */ + final BiFunction, L> loggerSupplier; + + + public LazyLoggerFactories(BiFunction, L> loggerSupplier) { + this(Objects.requireNonNull(loggerSupplier), + (Void)null); + } + + private LazyLoggerFactories(BiFunction, L> loggerSupplier, + Void unused) { + this.loggerSupplier = loggerSupplier; + } + + } + + static interface LoggerAccessor { + /** + * The logger name. + * @return The name of the logger that is / will be lazily created. + */ + public String getLoggerName(); + + /** + * Returns the wrapped logger object. + * @return the wrapped logger object. + */ + public Logger wrapped(); + + /** + * A PlatformLoggerBridge view of the wrapped logger object. + * @return A PlatformLoggerBridge view of the wrapped logger object. + */ + public PlatformLoggerBridge platform(); + } + + /** + * The LazyLoggerAccessor class holds all the logic that delays the creation + * of the SPI logger until such a time that the VM is booted and the logger + * is actually used for logging. + * + * This class uses the services of the BootstrapLogger class to instantiate + * temporary loggers if appropriate. + * + * @since 9 + * + */ + static final class LazyLoggerAccessor implements LoggerAccessor { + + // The factories that will be used to create the logger lazyly + final LazyLoggerFactories factories; + + // We need to pass the actual caller when creating the logger. + private final WeakReference> callerRef; + + // The name of the logger that will be created lazyly + final String name; + // The plain logger SPI object - null until it is accessed for the + // first time. + private volatile Logger w; + // A PlatformLoggerBridge view of w. + private volatile PlatformLoggerBridge p; + + + private LazyLoggerAccessor(String name, + LazyLoggerFactories factories, + Class caller) { + this(Objects.requireNonNull(name), Objects.requireNonNull(factories), + Objects.requireNonNull(caller), null); + } + + private LazyLoggerAccessor(String name, + LazyLoggerFactories factories, + Class caller, Void unused) { + this.acc = AccessController.getContext(); // since the creation of the + // actual logger is delayed we + // want to capture the context + // in which the logger should + // be created. + this.name = name; + this.factories = factories; + this.callerRef = new WeakReference>(caller); + } + + /** + * The logger name. + * @return The name of the logger that is / will be lazily created. + */ + @Override + public String getLoggerName() { + return name; + } + + // must be called in synchronized block + // set wrapped logger if not set + private void setWrappedIfNotSet(Logger wrapped) { + if (w == null) { + w = wrapped; + } + } + + /** + * Returns the logger SPI object, creating it if 'w' is still null. + * @return the logger SPI object. + */ + public Logger wrapped() { + Logger wrapped = w; + if (wrapped != null) return wrapped; + // Wrapped logger not created yet: create it. + // BootstrapLogger has the logic to decide whether to invoke the + // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) + // logger. + wrapped = BootstrapLogger.getLogger(this); + synchronized(this) { + // if w has already been in between, simply drop 'wrapped'. + setWrappedIfNotSet(wrapped); + return w; + } + } + + /** + * A PlatformLoggerBridge view of the wrapped logger. + * @return A PlatformLoggerBridge view of the wrapped logger. + */ + public PlatformLoggerBridge platform() { + // We can afford to return the platform view of the previous + // logger - if that view is not null. + // Because that view will either be the BootstrapLogger, which + // will redirect to the new wrapper properly, or the temporary + // logger - which in effect is equivalent to logging something + // just before the application initialized LogManager. + PlatformLoggerBridge platform = p; + if (platform != null) return platform; + synchronized (this) { + if (w != null) { + if (p == null) p = PlatformLoggerBridge.convert(w); + return p; + } + } + // If we reach here it means that the wrapped logger may not + // have been created yet: attempt to create it. + // BootstrapLogger has the logic to decide whether to invoke the + // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) + // logger. + final Logger wrapped = BootstrapLogger.getLogger(this); + synchronized(this) { + // if w has already been set, simply drop 'wrapped'. + setWrappedIfNotSet(wrapped); + if (p == null) p = PlatformLoggerBridge.convert(w); + return p; + } + } + + /** + * Makes this accessor release a temporary logger. + * This method is called + * by BootstrapLogger when JUL is the default backend and LogManager + * is initialized, in order to replace temporary SimpleConsoleLoggers by + * real JUL loggers. See BootstrapLogger for more details. + * If {@code replace} is {@code true}, then this method will force + * the accessor to eagerly recreate its wrapped logger. + * Note: passing {@code replace=false} is no guarantee that the + * method will not actually replace the released logger. + * @param temporary The temporary logger too be released. + * @param replace Whether the released logger should be eagerly + * replaced. + */ + void release(SimpleConsoleLogger temporary, boolean replace) { + ConfigurableLoggerBridge.LoggerConfiguration conf = + ConfigurableLoggerBridge.getLoggerConfiguration(temporary); + PlatformLogger.Level level = conf != null + ? conf.getPlatformLevel() + : null; + synchronized (this) { + if (this.w == temporary) { + this.w = null; this.p = null; + } + } + PlatformLoggerBridge platform = replace || level != null + ? this.platform() : null; + + if (level != null) { + conf = (platform != null && platform != temporary) + ? ConfigurableLoggerBridge.getLoggerConfiguration(platform) + : null; + if (conf != null) conf.setPlatformLevel(level); + } + } + + /** + * Replace 'w' by the real SPI logger and flush the log messages pending + * in the temporary 'bootstrap' Logger. Called by BootstrapLogger when + * this accessor's bootstrap logger is accessed and BootstrapLogger + * notices that the VM is no longer booting. + * @param bootstrap This accessor's bootstrap logger (usually this is 'w'). + */ + void flush(BootstrapLogger bootstrap) { + synchronized(this) { + // another thread may have already invoked flush() + if (this.w == bootstrap) { + this.w = null; this.p = null; + } + } + bootstrap.flush(this.wrapped()); + } + + // Creates the wrapped logger by invoking the SPI. + Logger createLogger() { + return get(factories.loggerSupplier); + } + + /** + * Creates a new lazy logger accessor for the named logger. The given + * factories will be use when it becomes necessary to actually create + * the logger. + * @param An interface that extends {@link Logger}. + * @param name The logger name. + * @param factories The factories that should be used to create the + * wrapped logger. + * @return A new LazyLoggerAccessor. + */ + public static LazyLoggerAccessor makeAccessor(String name, + LazyLoggerFactories factories, Class caller) { + return new LazyLoggerAccessor(name, factories, caller); + } + + + // The context that should be used to create and map the logger. + final AccessControlContext acc; + + private Logger get(final BiFunction, ? extends Logger> s) { + final Class caller = callerRef.get(); + if (caller == null) { + throw new IllegalStateException("The class for which this logger" + + " was created has been garbage collected"); + } + SecurityManager sm = System.getSecurityManager(); + if (sm == null || acc == null) { + return s.apply(name, caller); + } else { + // not sure we can actually use lambda here. So may need to create + // an anonymous class to replace the lambda; + PrivilegedAction pa = () -> s.apply(name, caller); + return AccessController.doPrivileged(pa , acc); + } + } + + } + + /** + * An implementation of {@link Logger} that redirects all calls to a wrapped + * instance of {@code Logger}. + * + * @since 9 + * + */ + private static class LazyLoggerWrapper + extends AbstractLoggerWrapper { + + final LoggerAccessor loggerAccessor; + + public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) { + this(Objects.requireNonNull(loggerSinkSupplier), (Void)null); + } + + private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier, + Void unused) { + this.loggerAccessor = loggerSinkSupplier; + } + + @Override + final Logger wrapped() { + return loggerAccessor.wrapped(); + } + + @Override + PlatformLoggerBridge platformProxy() { + return loggerAccessor.platform(); + } + + } + + // Do not expose this outside of this package. + private static volatile LoggerFinder provider = null; + private static LoggerFinder accessLoggerFinder() { + if (provider == null) { + // no need to lock: it doesn't matter if we call + // getLoggerFinder() twice - since LoggerFinder already caches + // the result. + // This is just an optimization to avoid the cost of calling + // doPrivileged every time. + final SecurityManager sm = System.getSecurityManager(); + provider = sm == null ? LoggerFinder.getLoggerFinder() : + AccessController.doPrivileged( + (PrivilegedAction)LoggerFinder::getLoggerFinder); + } + return provider; + } + + // Avoid using lambda here as lazy loggers could be created early + // in the bootstrap sequence... + private static final BiFunction, Logger> loggerSupplier = + new BiFunction, Logger>() { + @Override + public Logger apply(String name, Class caller) { + return LazyLoggers.getLoggerFromFinder(name, caller); + } + }; + + private static final LazyLoggerFactories factories = + new LazyLoggerFactories<>(loggerSupplier); + + + + // A concrete implementation of Logger that delegates to a System.Logger, + // but only creates the System.Logger instance lazily when it's used for + // the first time. + // The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies + // on the logic embedded in BootstrapLogger to avoid loading the concrete + // logger provider until the VM has finished booting. + // + private static final class JdkLazyLogger extends LazyLoggerWrapper { + JdkLazyLogger(String name, Class caller) { + this(LazyLoggerAccessor.makeAccessor(name, factories, caller), + (Void)null); + } + private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) { + super(holder); + } + } + + /** + * Gets a logger from the LoggerFinder. Creates the actual concrete + * logger. + * @param name name of the logger + * @param caller class on behalf of which the logger is created + * @return The logger returned by the LoggerFinder. + */ + static Logger getLoggerFromFinder(String name, Class caller) { + final SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + return accessLoggerFinder().getLogger(name, caller); + } else { + return AccessController.doPrivileged((PrivilegedAction) + () -> {return accessLoggerFinder().getLogger(name, caller);}, + null, LoggerFinder.LOGGERFINDER_PERMISSION); + } + } + + /** + * Returns a (possibly lazy) Logger for the caller. + * + * @param name the logger name + * @param caller The class on behalf of which the logger is created. + * If the caller is not loaded from the Boot ClassLoader, + * the LoggerFinder is accessed and the logger returned + * by {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class)} + * is returned to the caller directly. + * Otherwise, the logger returned by + * {@link #getLazyLogger(java.lang.String, java.lang.Class)} + * is returned to the caller. + * + * @return a (possibly lazy) Logger instance. + */ + // @CallerSensitive + public static final Logger getLogger(String name, Class caller) { + // final SecurityManager sm = System.getSecurityManager(); + // if (sm != null) { + // // this permission check may not be needed if we rely + // // on the fact that this class is a non accessible sun.* class + // Class immediateCaller = Reflection.getCallerClass(); + // if (immediateCaller.getClassLoader() != null) { + // sm.checkPermission(LoggerFinder.CONTROL_PERMISSION); + // } + // } + if (caller.getClassLoader() == null) { + return getLazyLogger(name, caller); + } else { + return getLoggerFromFinder(name, caller); + } + } + + /** + * Returns a (possibly lazy) Logger suitable for system classes. + * Whether the returned logger is lazy or not depend on the result + * returned by {@link BootstrapLogger#useLazyLoggers()}. + * + * @param name the logger name + * @param caller the class on behalf of which the logger is created. + * @return a (possibly lazy) Logger instance. + */ + // @CallerSensitive + public static final Logger getLazyLogger(String name, Class caller) { + + // final SecurityManager sm = System.getSecurityManager(); + // if (sm != null) { + // // this permission check may not be needed if we rely + // // on the fact that this class is a non accessible sun.* class + // Class immediateCaller = Reflection.getCallerClass(); + // if (immediateCaller.getClassLoader() != null) { + // sm.checkPermission(LoggerFinder.CONTROL_PERMISSION); + // } + // } + + // BootstrapLogger has the logic to determine whether a LazyLogger + // should be used. Usually, it is worth it only if: + // - the VM is not yet booted + // - or, the backend is JUL and there is no configuration + // - or, the backend is a custom backend, as we don't know what + // that is going to load... + // So if for instance the VM is booted and we use JUL with a custom + // configuration, we're not going to delay the creation of loggers... + final boolean useLazyLogger = BootstrapLogger.useLazyLoggers(); + if (useLazyLogger) { + return new JdkLazyLogger(name, caller); + } else { + // Directly invoke the LoggerFinder. + return getLoggerFromFinder(name, caller); + } + } + +}