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      */
  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 caller.
 402      * @param name the logger name.
 403      * @param caller the caller for which the logger should be created.
 404      * @return a Logger suitable for use in the given caller.
 405      */
 406     private static java.util.logging.Logger demandJULLoggerFor(final String name,
 407                                                             /* Module */
 408                                                             final Class<?> caller) {
 409         final LogManager manager = LogManager.getLogManager();
 410         final SecurityManager sm = System.getSecurityManager();
 411         if (sm == null) {
 412             return logManagerAccess.demandLoggerFor(manager, name, caller);
 413         } else {
 414             final PrivilegedAction<java.util.logging.Logger> pa =
 415                     () -> logManagerAccess.demandLoggerFor(manager, name, caller);
 416             return AccessController.doPrivileged(pa, null, LOGGING_CONTROL_PERMISSION);
 417         }
 418     }
 419 
 420     /**
 421      * {@inheritDoc}
 422      *
 423      * @apiNote The logger returned by this method can be configured through
 424      * its {@linkplain java.util.logging.LogManager#getLogger(String)
 425      * corresponding java.util.logging.Logger backend}.
 426      *
 427      * @return {@inheritDoc}
 428      * @throws SecurityException if the calling code doesn't have the
 429      * {@code RuntimePermission("loggerFinder")}.
 430      */
 431     @Override
 432     protected Logger demandLoggerFor(String name, /* Module */ Class<?> caller) {
 433         final SecurityManager sm = System.getSecurityManager();
 434         if (sm != null) {
 435             sm.checkPermission(LOGGERFINDER_PERMISSION);
 436         }
 437         return JULWrapper.of(demandJULLoggerFor(name,caller));
 438     }
 439 
 440     public static interface LogManagerAccess {
 441         java.util.logging.Logger demandLoggerFor(LogManager manager,
 442                 String name, /* Module */ Class<?> caller);
 443     }
 444 
 445     // Hook for tests
 446     public static LogManagerAccess getLogManagerAccess() {
 447         final SecurityManager sm = System.getSecurityManager();
 448         if (sm != null) {
 449             sm.checkPermission(LOGGING_CONTROL_PERMISSION);
 450         }
 451         // Triggers initialization of accessJulLogger if not set.
 452         if (logManagerAccess == null) LogManager.getLogManager();
 453         return logManagerAccess;
 454     }
 455 
 456 
 457     private static volatile LogManagerAccess logManagerAccess;
 458     public static void setLogManagerAccess(LogManagerAccess accesLoggers) {
 459         final SecurityManager sm = System.getSecurityManager();
 460         if (sm != null) {
 461             sm.checkPermission(LOGGING_CONTROL_PERMISSION);
 462         }
 463         logManagerAccess = accesLoggers;
 464     }
 465 
 466 }