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