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 
27 package sun.util.logging.internal;
28 
29 import java.security.AccessController;
30 import java.security.PrivilegedAction;
31 import java.util.ResourceBundle;
32 import java.util.function.Supplier;
33 import java.lang.System.LoggerFinder;
34 import java.lang.System.Logger;
35 import java.util.Objects;
36 import java.util.logging.LogManager;
37 import jdk.internal.logger.DefaultLoggerFinder;
38 import java.util.logging.LoggingPermission;
39 import sun.util.logging.PlatformLogger;
40 import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
41 
42 /**
43  * This {@code LoggingProviderImpl} is the JDK internal implementation of the
44  * {@link jdk.internal.logger.DefaultLoggerFinder} which is used by
45  * the default implementation of the {@link Logger}
46  * when no {@link LoggerFinder} is found
47  * and {@code java.util.logging} is present.
48  * When {@code java.util.logging} is present, the {@code LoggingProviderImpl}
49  * is {@linkplain java.util.ServiceLoader#loadInstalled(Class) installed} as
50  * an internal service provider, making it possible to use {@code java.util.logging}
51  * as the backend for loggers returned by the default LoggerFinder implementation.
52  * <p>
53  * This implementation of {@link DefaultLoggerFinder} returns instances of
54  * {@link java.lang.System.Logger} which
55  * delegate to a wrapped instance of {@link java.util.logging.Logger
56  * java.util.logging.Logger}.
57  * <br>
58  * Loggers returned by this class can therefore be configured by accessing
59  * their wrapped implementation through the regular {@code java.util.logging}
60  * APIs - such as {@link java.util.logging.LogManager java.util.logging.LogManager}
61  * and {@link java.util.logging.Logger java.util.logging.Logger}.
62  *
63  * @apiNote Programmers are not expected to call this class directly.
64  * Instead they should rely on the static methods defined by
65  * {@link java.lang.System java.lang.System}.
66  * <p>
67  * To replace this default
68  * {@code java.util.logging} backend, an application is expected to install
69  * its own {@link java.lang.System.LoggerFinder}.
70  *
71  * @see java.lang.System.Logger
72  * @see java.lang.System.LoggerFinder
73  * @see sun.util.logging.PlatformLogger.Bridge
74  * @see java.lang.System
75  * @see jdk.internal.logger
76  * @see jdk.internal.logger
77  *
78  */
79 public final class LoggingProviderImpl extends DefaultLoggerFinder {
80     static final RuntimePermission LOGGERFINDER_PERMISSION =
81                 new RuntimePermission("loggerFinder");
82     private static final LoggingPermission LOGGING_CONTROL_PERMISSION =
83             new LoggingPermission("control", null);
84 
85     /**
86      * Creates a new instance of LoggingProviderImpl.
87      * @throws SecurityException if the calling code does not have the
88      * {@code RuntimePermission("loggerFinder")}.
89      */
LoggingProviderImpl()90     public LoggingProviderImpl() {
91     }
92 
93     /**
94      * A logger that delegates to a java.util.logging.Logger delegate.
95      */
96     static final class JULWrapper extends LoggerConfiguration
97             implements System.Logger, PlatformLogger.Bridge,
98                        PlatformLogger.ConfigurableBridge {
99 
100 
101         private static final java.util.logging.Level[] spi2JulLevelMapping = {
102                 java.util.logging.Level.ALL,     // mapped from ALL
103                 java.util.logging.Level.FINER,   // mapped from TRACE
104                 java.util.logging.Level.FINE,    // mapped from DEBUG
105                 java.util.logging.Level.INFO,    // mapped from INFO
106                 java.util.logging.Level.WARNING, // mapped from WARNING
107                 java.util.logging.Level.SEVERE,  // mapped from ERROR
108                 java.util.logging.Level.OFF      // mapped from OFF
109         };
110 
111         private static final java.util.logging.Level[] platform2JulLevelMapping = {
112                 java.util.logging.Level.ALL,     // mapped from ALL
113                 java.util.logging.Level.FINEST,  // mapped from FINEST
114                 java.util.logging.Level.FINER,   // mapped from FINER
115                 java.util.logging.Level.FINE,    // mapped from FINE
116                 java.util.logging.Level.CONFIG,  // mapped from CONFIG
117                 java.util.logging.Level.INFO,    // mapped from INFO
118                 java.util.logging.Level.WARNING, // mapped from WARNING
119                 java.util.logging.Level.SEVERE,  // mapped from SEVERE
120                 java.util.logging.Level.OFF      // mapped from OFF
121         };
122 
123         private final java.util.logging.Logger julLogger;
124 
125 
JULWrapper(java.util.logging.Logger logger)126         private JULWrapper(java.util.logging.Logger logger) {
127             this.julLogger = logger;
128         }
129 
130         @Override
getName()131         public String getName() {
132             return julLogger.getName();
133         }
134         @Override
log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable throwable)135         public void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable throwable) {
136             julLogger.log(toJUL(level), msg, throwable);
137         }
138 
139         @Override
log(sun.util.logging.PlatformLogger.Level level, String format, Object... params)140         public void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) {
141             julLogger.log(toJUL(level), format, params);
142         }
143 
144         @Override
log(sun.util.logging.PlatformLogger.Level level, String msg)145         public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
146             julLogger.log(toJUL(level), msg);
147         }
148 
149         @Override
log(sun.util.logging.PlatformLogger.Level level, Supplier<String> msgSuppier)150         public void log(sun.util.logging.PlatformLogger.Level level, Supplier<String> msgSuppier) {
151             julLogger.log(toJUL(level), msgSuppier);
152         }
153 
154         @Override
log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, Supplier<String> msgSuppier)155         public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, Supplier<String> msgSuppier) {
156             julLogger.log(toJUL(level), thrown, msgSuppier);
157         }
158 
159         @Override
logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Throwable throwable)160         public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Throwable throwable) {
161             julLogger.logrb(toJUL(level), bundle, key, throwable);
162         }
163 
164         @Override
logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Object... params)165         public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Object... params) {
166             julLogger.logrb(toJUL(level), bundle, key, params);
167         }
168 
169         @Override
logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg)170         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg) {
171             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg);
172         }
173 
174         @Override
logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, Supplier<String> msgSupplier)175         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
176                 Supplier<String> msgSupplier) {
177             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msgSupplier);
178         }
179 
180         @Override
logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg, Object... params)181         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
182                 String msg, Object... params) {
183             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, params);
184         }
185 
186         @Override
logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg, Throwable thrown)187         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
188                 String msg, Throwable thrown) {
189             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, thrown);
190         }
191 
192         @Override
logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, Throwable thrown, Supplier<String> msgSupplier)193         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
194                 Throwable thrown, Supplier<String> msgSupplier) {
195             julLogger.logp(toJUL(level), sourceClass, sourceMethod,
196                     thrown, msgSupplier);
197         }
198 
199         @Override
logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String key, Object... params)200         public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
201                 ResourceBundle bundle, String key, Object... params) {
202             julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
203                     bundle, key, params);
204         }
205 
206         @Override
logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String key, Throwable thrown)207         public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
208                 ResourceBundle bundle, String key, Throwable thrown) {
209             julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
210                     bundle, key, thrown);
211         }
212 
213         @Override
isLoggable(sun.util.logging.PlatformLogger.Level level)214         public  boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
215             return julLogger.isLoggable(toJUL(level));
216         }
217 
218         // -----------------------------------------------------------------
219         // Generic methods taking a Level as parameter
220         // -----------------------------------------------------------------
221 
222 
223         @Override
isLoggable(Level level)224         public boolean isLoggable(Level level) {
225             return julLogger.isLoggable(toJUL(level));
226         }
227 
228         @Override
log(Level level, String msg)229         public void log(Level level, String msg) {
230             julLogger.log(toJUL(level), msg);
231         }
232 
233         @Override
log(Level level, Supplier<String> msgSupplier)234         public void log(Level level,
235                         Supplier<String> msgSupplier) {
236             // We need to check for null here to satisfy the contract
237             // of System.Logger - because the underlying implementation
238             // of julLogger will check for it only if the level is
239             // loggable
240             Objects.requireNonNull(msgSupplier);
241             julLogger.log(toJUL(level), msgSupplier);
242         }
243 
244         @Override
log(Level level, Object obj)245         public void log(Level level, Object obj) {
246             // We need to check for null here to satisfy the contract
247             // of System.Logger - because the underlying implementation
248             // of julLogger will check for it only if the level is
249             // loggable
250             Objects.requireNonNull(obj);
251             julLogger.log(toJUL(level), () -> obj.toString());
252         }
253 
254         @Override
log(Level level, String msg, Throwable thrown)255         public void log(Level level,
256                         String msg, Throwable thrown) {
257             julLogger.log(toJUL(level), msg, thrown);
258         }
259 
260         @Override
log(Level level, Supplier<String> msgSupplier, Throwable thrown)261         public void log(Level level, Supplier<String> msgSupplier,
262                         Throwable thrown) {
263             // We need to check for null here to satisfy the contract
264             // of System.Logger - because the underlying implementation
265             // of julLogger will check for it only if the level is
266             // loggable
267             Objects.requireNonNull(msgSupplier);
268             julLogger.log(toJUL(level), thrown, msgSupplier);
269         }
270 
271         @Override
log(Level level, String format, Object... params)272         public void log(Level level,
273                         String format, Object... params) {
274             julLogger.log(toJUL(level), format, params);
275         }
276 
277         @Override
log(Level level, ResourceBundle bundle, String key, Throwable thrown)278         public void log(Level level, ResourceBundle bundle,
279                         String key, Throwable thrown) {
280             julLogger.logrb(toJUL(level), bundle, key, thrown);
281         }
282 
283         @Override
log(Level level, ResourceBundle bundle, String format, Object... params)284         public void log(Level level, ResourceBundle bundle,
285                         String format, Object... params) {
286             julLogger.logrb(toJUL(level), bundle, format, params);
287         }
288 
toJUL(Level level)289         static java.util.logging.Level toJUL(Level level) {
290             if (level == null) return null;
291             assert level.ordinal() < spi2JulLevelMapping.length;
292             return spi2JulLevelMapping[level.ordinal()];
293         }
294 
295         // ---------------------------------------------------------
296         // Methods from PlatformLogger.Bridge
297         // ---------------------------------------------------------
298 
299         @Override
300         public boolean isEnabled() {
301             return julLogger.getLevel() != java.util.logging.Level.OFF;
302         }
303 
304         @Override
305         public PlatformLogger.Level getPlatformLevel() {
306             final java.util.logging.Level javaLevel = julLogger.getLevel();
307             if (javaLevel == null) return null;
308             try {
309                 return PlatformLogger.Level.valueOf(javaLevel.getName());
310             } catch (IllegalArgumentException e) {
311                 return PlatformLogger.Level.valueOf(javaLevel.intValue());
312             }
313         }
314 
315         @Override
316         public void setPlatformLevel(PlatformLogger.Level level) {
317             // null is allowed here
318             julLogger.setLevel(toJUL(level));
319         }
320 
321         @Override
322         public LoggerConfiguration getLoggerConfiguration() {
323             return this;
324         }
325 
326         static java.util.logging.Level toJUL(PlatformLogger.Level level) {
327             // The caller will throw if null is invalid in its context.
328             // There's at least one case where a null level is valid.
329             if (level == null) return null;
330             assert level.ordinal() < platform2JulLevelMapping.length;
331             return platform2JulLevelMapping[level.ordinal()];
332         }
333 
334         @Override
335         public boolean equals(Object obj) {
336             return (obj instanceof JULWrapper)
337                     && obj.getClass() == this.getClass()
338                     && ((JULWrapper)obj).julLogger == this.julLogger;
339         }
340 
341         @Override
342         public int hashCode() {
343             return julLogger.hashCode();
344         }
345 
346         // A JULWrapper is just a stateless thin shell over a JUL logger - so
347         // for a given JUL logger, we could always return the same wrapper.
348         //
349         // This is an optimization which may - or may not - be worth the
350         // trouble: if many classes use the same logger, and if each class
351         // keeps a reference to that logger, then caching the wrapper will
352         // be worthwhile. Otherwise, if each logger is only referred once,
353         // then the cache will eat up more memory than would be necessary...
354         //
355         // Here is an example of how we could implement JULWrapper.of(...)
356         // if we wanted to create at most one wrapper instance for each logger
357         // instance:
358         //
359         //        static final WeakHashMap<JULWrapper, WeakReference<JULWrapper>>
360         //                wrappers = new WeakHashMap<>();
361         //
362         //        static JULWrapper of(java.util.logging.Logger logger) {
363         //
364         //            // First access without synchronizing
365         //            final JULWrapper candidate = new JULWrapper(logger);
366         //            WeakReference<JULWrapper> ref = wrappers.get(candidate);
367         //            JULWrapper found = ref.get();
368         //
369         //            // OK - we found it - lets return it.
370         //            if (found != null) return found;
371         //
372         //            // Not found. Need to synchronize.
373         //            synchronized (wrappers) {
374         //                ref = wrappers.get(candidate);
375         //                found = ref.get();
376         //                if (found == null) {
377         //                    wrappers.put(candidate, new WeakReference<>(candidate));
378         //                    found = candidate;
379         //                }
380         //            }
381         //            assert found != null;
382         //            return found;
383         //        }
384         //
385         // But given that it may end up eating more memory in the nominal case
386         // (where each class that does logging has its own logger with the
387         //  class name as logger name and stashes that logger away in a static
388         //  field, thus making the cache redundant - as only one wrapper will
389         //  ever be created anyway) - then we will simply return a new wrapper
390         // for each invocation of JULWrapper.of(...) - which may
391         // still prove more efficient in terms of memory consumption...
392         //
393         static JULWrapper of(java.util.logging.Logger logger) {
394             return new JULWrapper(logger);
395         }
396 
397 
398     }
399 
400     /**
401      * Creates a java.util.logging.Logger for the given module.
402      * @param name the logger name.
403      * @param module the module for which the logger should be created.
404      * @return a Logger suitable for use in the given module.
405      */
406     private static java.util.logging.Logger demandJULLoggerFor(final String name,
407                                                                Module module) {
408         final LogManager manager = LogManager.getLogManager();
409         final SecurityManager sm = System.getSecurityManager();
410         if (sm == null) {
411             return logManagerAccess.demandLoggerFor(manager, name, module);
412         } else {
413             final PrivilegedAction<java.util.logging.Logger> pa =
414                     () -> logManagerAccess.demandLoggerFor(manager, name, module);
415             return AccessController.doPrivileged(pa, null, LOGGING_CONTROL_PERMISSION);
416         }
417     }
418 
419     /**
420      * {@inheritDoc}
421      *
422      * @apiNote The logger returned by this method can be configured through
423      * its {@linkplain java.util.logging.LogManager#getLogger(String)
424      * corresponding java.util.logging.Logger backend}.
425      *
426      * @return {@inheritDoc}
427      * @throws SecurityException if the calling code doesn't have the
428      * {@code RuntimePermission("loggerFinder")}.
429      */
430     @Override
431     protected Logger demandLoggerFor(String name, Module module) {
432         final SecurityManager sm = System.getSecurityManager();
433         if (sm != null) {
434             sm.checkPermission(LOGGERFINDER_PERMISSION);
435         }
436         return JULWrapper.of(demandJULLoggerFor(name,module));
437     }
438 
439     public static interface LogManagerAccess {
440         java.util.logging.Logger demandLoggerFor(LogManager manager,
441                 String name, Module module);
442     }
443 
444     // Hook for tests
445     public static LogManagerAccess getLogManagerAccess() {
446         final SecurityManager sm = System.getSecurityManager();
447         if (sm != null) {
448             sm.checkPermission(LOGGING_CONTROL_PERMISSION);
449         }
450         // Triggers initialization of accessJulLogger if not set.
451         if (logManagerAccess == null) LogManager.getLogManager();
452         return logManagerAccess;
453     }
454 
455 
456     private static volatile LogManagerAccess logManagerAccess;
457     public static void setLogManagerAccess(LogManagerAccess accesLoggers) {
458         final SecurityManager sm = System.getSecurityManager();
459         if (sm != null) {
460             sm.checkPermission(LOGGING_CONTROL_PERMISSION);
461         }
462         logManagerAccess = accesLoggers;
463     }
464 
465 }
466