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
27package sun.util.logging.internal;
28
29import java.security.AccessController;
30import java.security.PrivilegedAction;
31import java.util.ResourceBundle;
32import java.util.function.Supplier;
33import java.lang.System.LoggerFinder;
34import java.lang.System.Logger;
35import java.util.Objects;
36import java.util.logging.LogManager;
37import jdk.internal.logger.DefaultLoggerFinder;
38import java.util.logging.LoggingPermission;
39import sun.util.logging.PlatformLogger;
40import 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 */
79public 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     */
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
126        private JULWrapper(java.util.logging.Logger logger) {
127            this.julLogger = logger;
128        }
129
130        @Override
131        public String getName() {
132            return julLogger.getName();
133        }
134        @Override
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
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
145        public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
146            julLogger.log(toJUL(level), msg);
147        }
148
149        @Override
150        public void log(sun.util.logging.PlatformLogger.Level level, Supplier<String> msgSuppier) {
151            julLogger.log(toJUL(level), msgSuppier);
152        }
153
154        @Override
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
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
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
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
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
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
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
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
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
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
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
224        public boolean isLoggable(Level level) {
225            return julLogger.isLoggable(toJUL(level));
226        }
227
228        @Override
229        public void log(Level level, String msg) {
230            julLogger.log(toJUL(level), msg);
231        }
232
233        @Override
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
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
255        public void log(Level level,
256                        String msg, Throwable thrown) {
257            julLogger.log(toJUL(level), msg, thrown);
258        }
259
260        @Override
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
272        public void log(Level level,
273                        String format, Object... params) {
274            julLogger.log(toJUL(level), format, params);
275        }
276
277        @Override
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
284        public void log(Level level, ResourceBundle bundle,
285                        String format, Object... params) {
286            julLogger.logrb(toJUL(level), bundle, format, params);
287        }
288
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