1 /*
   2  * Copyright (c) 2014, 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 sun.util.logger.JdkLoggerProvider;
  36 import java.util.logging.LoggingPermission;
  37 import sun.util.logging.ConfigurableLoggerBridge;
  38 import sun.util.logging.PlatformLogger;
  39 import sun.util.logging.ConfigurableLoggerBridge.LoggerConfiguration;
  40 import sun.util.logging.PlatformLoggerBridge;
  41 
  42 /**
  43  * This {@code JdkLoggingProvider} is the JDK internal implementation of the
  44  * {@link sun.util.logger.JdkLoggerProvider} 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 JdkLoggingProvider}
  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 JdkLoggerProvider} 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.PlatformLoggerBridge
  74  * @see java.lang.System
  75  * @see sun.util.logger
  76  * @see sun.util.logging.internal
  77  *
  78  */
  79 public final class JdkLoggingProvider extends JdkLoggerProvider {
  80     private static final RuntimePermission LOGGER_CONTROL_PERMISSION =
  81             LoggerFinder.LOGGERFINDER_PERMISSION;
  82     private static final LoggingPermission LOGGING_CONTROL_PERMISSION =
  83             new LoggingPermission("control", null);
  84     private static final LoggingPermission LOGGING_DEMANDLOGGER_PERMISSION =
  85             new LoggingPermission("demandLogger", null);
  86 
  87     /**
  88      * Creates a new instance of JdkLoggingProvider.
  89      * @throws SecurityException if the calling code does not have the
  90      * {@code RuntimePermission("loggerFinder")}.
  91      */
  92     public JdkLoggingProvider() {
  93         super();
  94     }
  95 
  96     /**
  97      * A logger that delegates to a java.util.logging.Logger delegate.
  98      */
  99     static final class JULWrapper extends LoggerConfiguration
 100             implements System.Logger, PlatformLoggerBridge,
 101                        ConfigurableLoggerBridge {
 102 
 103 
 104         private static final java.util.logging.Level[] spi2JulLevelMapping = {
 105                 java.util.logging.Level.ALL,     // mapped from ALL
 106                 java.util.logging.Level.FINER,   // mapped from TRACE
 107                 java.util.logging.Level.FINE,    // mapped from DEBUG
 108                 java.util.logging.Level.INFO,    // mapped from INFO
 109                 java.util.logging.Level.WARNING, // mapped from WARNING
 110                 java.util.logging.Level.SEVERE,  // mapped from ERROR
 111                 java.util.logging.Level.OFF      // mapped from OFF
 112         };
 113 
 114         private static final java.util.logging.Level[] platform2JulLevelMapping = {
 115                 java.util.logging.Level.ALL,     // mapped from ALL
 116                 java.util.logging.Level.FINEST,  // mapped from FINEST
 117                 java.util.logging.Level.FINER,   // mapped from FINER
 118                 java.util.logging.Level.FINE,    // mapped from FINE
 119                 java.util.logging.Level.CONFIG,  // mapped from CONFIG
 120                 java.util.logging.Level.INFO,    // mapped from INFO
 121                 java.util.logging.Level.WARNING, // mapped from WARNING
 122                 java.util.logging.Level.SEVERE,  // mapped from SEVERE
 123                 java.util.logging.Level.OFF      // mapped from OFF
 124         };
 125 
 126         private final java.util.logging.Logger julLogger;
 127 
 128 
 129         private JULWrapper(java.util.logging.Logger logger) {
 130             this.julLogger = logger;
 131         }
 132 
 133         @Override
 134         public String getName() {
 135             return julLogger.getName();
 136         }
 137         @Override
 138         public void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable throwable) {
 139             julLogger.log(toJUL(level), msg, throwable);
 140         }
 141 
 142         @Override
 143         public void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) {
 144             julLogger.log(toJUL(level), format, params);
 145         }
 146 
 147         @Override
 148         public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
 149             julLogger.log(toJUL(level), msg);
 150         }
 151 
 152         @Override
 153         public void log(sun.util.logging.PlatformLogger.Level level, Supplier<String> msgSuppier) {
 154             julLogger.log(toJUL(level), msgSuppier);
 155         }
 156 
 157         @Override
 158         public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, Supplier<String> msgSuppier) {
 159             julLogger.log(toJUL(level), thrown, msgSuppier);
 160         }
 161 
 162         @Override
 163         public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Throwable throwable) {
 164             julLogger.logrb(toJUL(level), bundle, key, throwable);
 165         }
 166 
 167         @Override
 168         public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Object... params) {
 169             julLogger.logrb(toJUL(level), bundle, key, params);
 170         }
 171 
 172         @Override
 173         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg) {
 174             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg);
 175         }
 176 
 177         @Override
 178         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
 179                 Supplier<String> msgSupplier) {
 180             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msgSupplier);
 181         }
 182 
 183         @Override
 184         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
 185                 String msg, Object... params) {
 186             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, params);
 187         }
 188 
 189         @Override
 190         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
 191                 String msg, Throwable thrown) {
 192             julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, thrown);
 193         }
 194 
 195         @Override
 196         public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
 197                 Throwable thrown, Supplier<String> msgSupplier) {
 198             julLogger.logp(toJUL(level), sourceClass, sourceMethod,
 199                     thrown, msgSupplier);
 200         }
 201 
 202         @Override
 203         public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
 204                 ResourceBundle bundle, String key, Object... params) {
 205             julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
 206                     bundle, key, params);
 207         }
 208 
 209         @Override
 210         public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
 211                 ResourceBundle bundle, String key, Throwable thrown) {
 212             julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
 213                     bundle, key, thrown);
 214         }
 215 
 216         @Override
 217         public  boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
 218             return julLogger.isLoggable(toJUL(level));
 219         }
 220 
 221         // -----------------------------------------------------------------
 222         // Generic methods taking a Level as parameter
 223         // -----------------------------------------------------------------
 224 
 225 
 226         @Override
 227         public boolean isLoggable(Level level) {
 228             return julLogger.isLoggable(toJUL(level));
 229         }
 230 
 231         @Override
 232         public void log(Level level, String msg) {
 233             julLogger.log(toJUL(level), msg);
 234         }
 235 
 236         @Override
 237         public void log(Level level,
 238                         Supplier<String> msgSupplier) {
 239             julLogger.log(toJUL(level), msgSupplier);
 240         }
 241 
 242         @Override
 243         public void log(Level level, Object obj) {
 244             julLogger.log(toJUL(level), () -> obj.toString());
 245         }
 246 
 247         @Override
 248         public void log(Level level,
 249                         String msg, Throwable thrown) {
 250             julLogger.log(toJUL(level), msg, thrown);
 251         }
 252 
 253         @Override
 254         public void log(Level level, Supplier<String> msgSupplier,
 255                         Throwable thrown) {
 256             julLogger.log(toJUL(level), thrown, msgSupplier);
 257         }
 258 
 259         @Override
 260         public void log(Level level,
 261                         String format, Object... params) {
 262             julLogger.log(toJUL(level), format, params);
 263         }
 264 
 265         @Override
 266         public void log(Level level, ResourceBundle bundle,
 267                         String key, Throwable thrown) {
 268             julLogger.logrb(toJUL(level), bundle, key, thrown);
 269         }
 270 
 271         @Override
 272         public void log(Level level, ResourceBundle bundle,
 273                         String format, Object... params) {
 274             julLogger.logrb(toJUL(level), bundle, format, params);
 275         }
 276 
 277         static java.util.logging.Level toJUL(Level level) {
 278             if (level == null) return null;
 279             assert level.ordinal() < spi2JulLevelMapping.length;
 280             return spi2JulLevelMapping[level.ordinal()];
 281         }
 282 
 283         // ---------------------------------------------------------
 284         // Methods from PlatformLoggerBridge
 285         // ---------------------------------------------------------
 286 
 287         @Override
 288         public boolean isEnabled() {
 289             return julLogger.getLevel() != java.util.logging.Level.OFF;
 290         }
 291 
 292         @Override
 293         public PlatformLogger.Level getPlatformLevel() {
 294             final java.util.logging.Level javaLevel = julLogger.getLevel();
 295             if (javaLevel == null) return null;
 296             try {
 297                 return PlatformLogger.Level.valueOf(javaLevel.getName());
 298             } catch (IllegalArgumentException e) {
 299                 return PlatformLogger.Level.valueOf(javaLevel.intValue());
 300             }
 301         }
 302 
 303         @Override
 304         public void setPlatformLevel(PlatformLogger.Level level) {
 305             julLogger.setLevel(toJUL(level));
 306         }
 307 
 308         @Override
 309         public LoggerConfiguration getLoggerConfiguration() {
 310             return this;
 311         }
 312 
 313         static java.util.logging.Level toJUL(PlatformLogger.Level level) {
 314             if (level == null) return null;
 315             assert level.ordinal() < platform2JulLevelMapping.length;
 316             return platform2JulLevelMapping[level.ordinal()];
 317         }
 318 
 319         @Override
 320         public boolean equals(Object obj) {
 321             return (obj instanceof JULWrapper)
 322                     && obj.getClass() == this.getClass()
 323                     && ((JULWrapper)obj).julLogger == this.julLogger;
 324         }
 325 
 326         @Override
 327         public int hashCode() {
 328             return julLogger.hashCode();
 329         }
 330 
 331         // A JULWrapper is just a stateless thin shell over a JUL logger - so
 332         // for a given JUL logger, we could always return the same wrapper.
 333         //
 334         // This is an optimization which may - or may not - be worth the
 335         // trouble: if many classes use the same logger, and if each class
 336         // keeps a reference to that logger, then caching the wrapper will
 337         // be worthwhile. Otherwise, if each logger is only referred once,
 338         // then the cache will eat up more memory than would be necessary...
 339         //
 340         // Here is an example of how we could implement JULWrapper.of(...)
 341         // if we wanted to create at most one wrapper instance for each logger
 342         // instance:
 343         //
 344         //        static final WeakHashMap<JULWrapper, WeakReference<JULWrapper>>
 345         //                wrappers = new WeakHashMap<>();
 346         //
 347         //        static JULWrapper of(java.util.logging.Logger logger) {
 348         //
 349         //            // First access without synchronizing
 350         //            final JULWrapper candidate = new JULWrapper(logger);
 351         //            WeakReference<JULWrapper> ref = wrappers.get(candidate);
 352         //            JULWrapper found = ref.get();
 353         //
 354         //            // OK - we found it - lets return it.
 355         //            if (found != null) return found;
 356         //
 357         //            // Not found. Need to synchronize.
 358         //            synchronized (wrappers) {
 359         //                ref = wrappers.get(candidate);
 360         //                found = ref.get();
 361         //                if (found == null) {
 362         //                    wrappers.put(candidate, new WeakReference<>(candidate));
 363         //                    found = candidate;
 364         //                }
 365         //            }
 366         //            assert found != null;
 367         //            return found;
 368         //        }
 369         //
 370         // But given that it may end up eating more memory in the nominal case
 371         // (where each class that does logging has its own logger with the
 372         //  class name as logger name and stashes that logger away in a static
 373         //  field, thus making the cache redundant - as only one wrapper will
 374         //  ever be created anyway) - then we will simply return a new wrapper
 375         // for each invocation of JULWrapper.of(...) - which may
 376         // still prove more efficient in terms of memory consumption...
 377         //
 378         static JULWrapper of(java.util.logging.Logger logger) {
 379             return new JULWrapper(logger);
 380         }
 381 
 382 
 383     }
 384 
 385     /**
 386      * Creates a java.util.logging.Logger for the given caller.
 387      * @param name the logger name.
 388      * @param caller the caller for which the logger should be created.
 389      * @return a Logger suitable for use in the given caller.
 390      */
 391     private static java.util.logging.Logger demandJULLoggerFor(final String name,
 392                                                             /* Module */
 393                                                             final Class<?> caller) {
 394         final SecurityManager sm = System.getSecurityManager();
 395         if (sm == null) {
 396             return java.util.logging.LogManager.demandLoggerFor(name, caller);
 397         } else {
 398             return AccessController.doPrivileged(
 399                     (PrivilegedAction<java.util.logging.Logger>)
 400                     () -> java.util.logging.LogManager.demandLoggerFor(name, caller),
 401                     null, LOGGING_DEMANDLOGGER_PERMISSION);
 402         }
 403     }
 404 
 405     /**
 406      * {@inheritDoc}
 407      *
 408      * @apiNote The logger returned by this method can be configured through
 409      * its {@linkplain java.util.logging.LogManager#getLogger(String)
 410      * corresponding java.util.logging.Logger backend}.
 411      *
 412      * @return {@inheritDoc}
 413      * @throws SecurityException if the calling code doesn't have the
 414      * {@code RuntimePermission("loggerFinder")}.
 415      */
 416     @Override
 417     protected Logger demandLoggerFor(String name, /* Module */ Class<?> caller) {
 418         final SecurityManager sm = System.getSecurityManager();
 419         if (sm != null) {
 420             sm.checkPermission(LOGGER_CONTROL_PERMISSION);
 421         }
 422         return JULWrapper.of(demandJULLoggerFor(name,caller));
 423     }
 424 
 425 }