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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.security.CodeSource;
  24 import java.security.Permission;
  25 import java.security.PermissionCollection;
  26 import java.security.Permissions;
  27 import java.security.Policy;
  28 import java.security.ProtectionDomain;
  29 import java.util.Arrays;
  30 import java.util.Collections;
  31 import java.util.Enumeration;
  32 import java.util.HashMap;
  33 import java.util.Map;
  34 import java.util.Objects;
  35 import java.util.Queue;
  36 import java.util.ResourceBundle;
  37 import java.util.concurrent.ArrayBlockingQueue;
  38 import java.util.concurrent.ConcurrentHashMap;
  39 import java.util.concurrent.atomic.AtomicBoolean;
  40 import java.util.concurrent.atomic.AtomicLong;
  41 import java.util.logging.Handler;
  42 import java.util.logging.LogManager;
  43 import java.util.logging.LogRecord;
  44 import java.lang.System.LoggerFinder;
  45 import sun.util.logging.PlatformLogger;
  46 
  47 /**
  48  * @test
  49  * @bug     8046565
  50  * @summary Tests all PlatformLogger methods with the default LoggerFinder JUL backend.
  51  * @modules java.base/sun.util.logging
  52  * @run  main/othervm DefaultPlatformLoggerTest
  53  * @author danielfuchs
  54  */
  55 public class DefaultPlatformLoggerTest {
  56 
  57     final static AtomicLong sequencer = new AtomicLong();
  58     final static boolean VERBOSE = false;
  59     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
  60         @Override
  61         protected AtomicBoolean initialValue() {
  62             return  new AtomicBoolean(false);
  63         }
  64     };
  65     static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
  66         @Override
  67         protected AtomicBoolean initialValue() {
  68             return  new AtomicBoolean(false);
  69         }
  70     };
  71 
  72     public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
  73 
  74     public static final class LogEvent implements Cloneable {
  75 
  76         public LogEvent() {
  77             this(sequencer.getAndIncrement());
  78         }
  79 
  80         LogEvent(long sequenceNumber) {
  81             this.sequenceNumber = sequenceNumber;
  82         }
  83 
  84         long sequenceNumber;
  85         boolean isLoggable;
  86         String loggerName;
  87         java.util.logging.Level level;
  88         ResourceBundle bundle;
  89         Throwable thrown;
  90         Object[] args;
  91         String msg;
  92         String className;
  93         String methodName;
  94 
  95         Object[] toArray() {
  96             return new Object[] {
  97                 sequenceNumber,
  98                 isLoggable,
  99                 loggerName,
 100                 level,
 101                 bundle,
 102                 thrown,
 103                 args,
 104                 msg,
 105                 className,
 106                 methodName,
 107             };
 108         }
 109 
 110         @Override
 111         public String toString() {
 112             return Arrays.deepToString(toArray());
 113         }
 114 
 115         @Override
 116         public boolean equals(Object obj) {
 117             return obj instanceof LogEvent
 118                     && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
 119         }
 120 
 121         @Override
 122         public int hashCode() {
 123             return Objects.hash(toArray());
 124         }
 125 
 126         public LogEvent cloneWith(long sequenceNumber)
 127                 throws CloneNotSupportedException {
 128             LogEvent cloned = (LogEvent)super.clone();
 129             cloned.sequenceNumber = sequenceNumber;
 130             return cloned;
 131         }
 132 
 133         public static LogEvent of(long sequenceNumber,
 134                 boolean isLoggable, String name,
 135                 java.util.logging.Level level, ResourceBundle bundle,
 136                 String key, Throwable thrown, Object... params) {
 137             return LogEvent.of(sequenceNumber, isLoggable, name,
 138                     DefaultPlatformLoggerTest.class.getName(),
 139                     "testLogger", level, bundle, key,
 140                     thrown, params);
 141         }
 142         public static LogEvent of(long sequenceNumber,
 143                 boolean isLoggable, String name,
 144                 String className, String methodName,
 145                 java.util.logging.Level level, ResourceBundle bundle,
 146                 String key, Throwable thrown, Object... params) {
 147             LogEvent evt = new LogEvent(sequenceNumber);
 148             evt.loggerName = name;
 149             evt.level = level;
 150             evt.args = params;
 151             evt.bundle = bundle;
 152             evt.thrown = thrown;
 153             evt.msg = key;
 154             evt.isLoggable = isLoggable;
 155             evt.className = className;
 156             evt.methodName = methodName;
 157             return evt;
 158         }
 159 
 160     }
 161 
 162     static final java.util.logging.Level[] julLevels = {
 163         java.util.logging.Level.ALL,
 164         new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
 165         java.util.logging.Level.FINEST,
 166         new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
 167         java.util.logging.Level.FINER,
 168         new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
 169         java.util.logging.Level.FINE,
 170         new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
 171         java.util.logging.Level.CONFIG,
 172         new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
 173         java.util.logging.Level.INFO,
 174         new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
 175         java.util.logging.Level.WARNING,
 176         new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
 177         java.util.logging.Level.SEVERE,
 178         new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
 179         java.util.logging.Level.OFF,
 180     };
 181 
 182     static final java.util.logging.Level[] julPlatformLevels = {
 183         java.util.logging.Level.FINEST,
 184         java.util.logging.Level.FINER,
 185         java.util.logging.Level.FINE,
 186         java.util.logging.Level.CONFIG,
 187         java.util.logging.Level.INFO,
 188         java.util.logging.Level.WARNING,
 189         java.util.logging.Level.SEVERE,
 190     };
 191 
 192 
 193     public static class MyBundle extends ResourceBundle {
 194 
 195         final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
 196 
 197         @Override
 198         protected Object handleGetObject(String key) {
 199             if (key.contains(" (translated)")) {
 200                 throw new RuntimeException("Unexpected key: " + key);
 201             }
 202             return map.computeIfAbsent(key, k -> k + " (translated)");
 203         }
 204 
 205         @Override
 206         public Enumeration<String> getKeys() {
 207             return Collections.enumeration(map.keySet());
 208         }
 209 
 210     }
 211 
 212     public static class MyHandler extends Handler {
 213 
 214         @Override
 215         public java.util.logging.Level getLevel() {
 216             return java.util.logging.Level.ALL;
 217         }
 218 
 219         @Override
 220         public void publish(LogRecord record) {
 221             eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
 222                     true, record.getLoggerName(),
 223                     record.getSourceClassName(),
 224                     record.getSourceMethodName(),
 225                     record.getLevel(),
 226                     record.getResourceBundle(), record.getMessage(),
 227                     record.getThrown(), record.getParameters()));
 228         }
 229         @Override
 230         public void flush() {
 231         }
 232         @Override
 233         public void close() throws SecurityException {
 234         }
 235 
 236     }
 237 
 238     public static class MyLoggerBundle extends MyBundle {
 239 
 240     }
 241 
 242     public static void main(String[] args) throws Exception {
 243         LoggerFinder provider = LoggerFinder.getLoggerFinder();
 244         java.util.logging.Logger appSink =
 245                 LogManager.demandLoggerFor("foo", DefaultPlatformLoggerTest.class);
 246         java.util.logging.Logger sysSink =
 247                 LogManager.demandLoggerFor("foo", Thread.class);
 248         appSink.addHandler(new MyHandler());
 249         sysSink.addHandler(new MyHandler());
 250         appSink.setUseParentHandlers(VERBOSE);
 251         sysSink.setUseParentHandlers(VERBOSE);
 252 
 253         System.out.println("\n*** Without Security Manager\n");
 254         test(provider, true, appSink, sysSink);
 255         System.out.println("Tetscase count: " + sequencer.get());
 256 
 257         Policy.setPolicy(new SimplePolicy(allowAll, allowControl));
 258         System.setSecurityManager(new SecurityManager());
 259 
 260         System.out.println("\n*** With Security Manager, without permissions\n");
 261         test(provider, false, appSink, sysSink);
 262         System.out.println("Tetscase count: " + sequencer.get());
 263 
 264         System.out.println("\n*** With Security Manager, with control permission\n");
 265         allowControl.get().set(true);
 266         test(provider, true, appSink, sysSink);
 267 
 268         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 269     }
 270 
 271     public static void test(LoggerFinder provider, boolean hasRequiredPermissions,
 272             java.util.logging.Logger appSink, java.util.logging.Logger sysSink) throws Exception {
 273 
 274         // No way to giva a resource bundle to a platform logger.
 275         // ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 276         final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
 277 
 278         PlatformLogger platform = PlatformLogger.getLogger("foo");
 279         loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
 280 
 281         testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
 282     }
 283 
 284     public static class Foo {
 285 
 286     }
 287 
 288     static void verbose(String msg) {
 289        if (VERBOSE) {
 290            System.out.println(msg);
 291        }
 292     }
 293 
 294     static void checkLogEvent(LoggerFinder provider, String desc,
 295             LogEvent expected) {
 296         LogEvent actual =  eventQueue.poll();
 297         if (!expected.equals(actual)) {
 298             throw new RuntimeException("mismatch for " + desc
 299                     + "\n\texpected=" + expected
 300                     + "\n\t  actual=" + actual);
 301         } else {
 302             verbose("Got expected results for "
 303                     + desc + "\n\t" + expected);
 304         }
 305     }
 306 
 307     static void checkLogEvent(LoggerFinder provider, String desc,
 308             LogEvent expected, boolean expectNotNull) {
 309         LogEvent actual =  eventQueue.poll();
 310         if (actual == null && !expectNotNull) return;
 311         if (actual != null && !expectNotNull) {
 312             throw new RuntimeException("Unexpected log event found for " + desc
 313                 + "\n\tgot: " + actual);
 314         }
 315         if (!expected.equals(actual)) {
 316             throw new RuntimeException("mismatch for " + desc
 317                     + "\n\texpected=" + expected
 318                     + "\n\t  actual=" + actual);
 319         } else {
 320             verbose("Got expected results for "
 321                     + desc + "\n\t" + expected);
 322         }
 323     }
 324 
 325     static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
 326         boolean before = allowAll.get().get();
 327         try {
 328             allowAll.get().set(true);
 329             sink.setLevel(loggerLevel);
 330         } finally {
 331             allowAll.get().set(before);
 332         }
 333     }
 334 
 335     // Calls the methods defined on LogProducer and verify the
 336     // parameters received by the underlying logger.
 337     private static void testLogger(LoggerFinder provider,
 338             Map<PlatformLogger, String> loggerDescMap,
 339             String name,
 340             ResourceBundle loggerBundle,
 341             PlatformLogger logger,
 342             java.util.logging.Logger sink) throws Exception {
 343 
 344         System.out.println("Testing " + loggerDescMap.get(logger));
 345         final java.util.logging.Level OFF = java.util.logging.Level.OFF;
 346 
 347         Foo foo = new Foo();
 348         String fooMsg = foo.toString();
 349         System.out.println("\tlogger.<level>(fooMsg)");
 350         for (java.util.logging.Level loggerLevel : julLevels) {
 351             setLevel(sink, loggerLevel);
 352             for (java.util.logging.Level messageLevel : julPlatformLevels) {
 353                 LogEvent expected =
 354                         LogEvent.of(
 355                             sequencer.get(),
 356                             loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
 357                             name, messageLevel, loggerBundle,
 358                             fooMsg, (Throwable)null, (Object[])null);
 359                 String desc2 = "logger." + messageLevel.toString().toLowerCase()
 360                         + "(fooMsg): loggerLevel="
 361                         + loggerLevel+", messageLevel="+messageLevel;
 362                 if (messageLevel == java.util.logging.Level.FINEST) {
 363                     logger.finest(fooMsg);
 364                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 365                 } else if (messageLevel == java.util.logging.Level.FINER) {
 366                     logger.finer(fooMsg);
 367                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 368                 } else if (messageLevel == java.util.logging.Level.FINE) {
 369                     logger.fine(fooMsg);
 370                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 371                 } else if (messageLevel == java.util.logging.Level.CONFIG) {
 372                     logger.config(fooMsg);
 373                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 374                 } else if (messageLevel == java.util.logging.Level.INFO) {
 375                     logger.info(fooMsg);
 376                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 377                 } else if (messageLevel == java.util.logging.Level.WARNING) {
 378                     logger.warning(fooMsg);
 379                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 380                 } else if (messageLevel == java.util.logging.Level.SEVERE) {
 381                     logger.severe(fooMsg);
 382                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 383                 }
 384             }
 385         }
 386 
 387         Throwable thrown = new Exception("OK: log me!");
 388         System.out.println("\tlogger.<level>(msg, thrown)");
 389         for (java.util.logging.Level loggerLevel : julLevels) {
 390             setLevel(sink, loggerLevel);
 391             for (java.util.logging.Level messageLevel :julPlatformLevels) {
 392                 LogEvent expected =
 393                         LogEvent.of(
 394                             sequencer.get(),
 395                             loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
 396                             name, messageLevel, loggerBundle,
 397                             fooMsg, thrown, (Object[])null);
 398                 String desc2 = "logger." + messageLevel.toString().toLowerCase()
 399                         + "(msg, thrown): loggerLevel="
 400                         + loggerLevel+", messageLevel="+messageLevel;
 401                 if (messageLevel == java.util.logging.Level.FINEST) {
 402                     logger.finest(fooMsg, thrown);
 403                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 404                 } else if (messageLevel == java.util.logging.Level.FINER) {
 405                     logger.finer(fooMsg, thrown);
 406                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 407                 } else if (messageLevel == java.util.logging.Level.FINE) {
 408                     logger.fine(fooMsg, thrown);
 409                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 410                 } else if (messageLevel == java.util.logging.Level.CONFIG) {
 411                     logger.config(fooMsg, thrown);
 412                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 413                 } else if (messageLevel == java.util.logging.Level.INFO) {
 414                     logger.info(fooMsg, thrown);
 415                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 416                 } else if (messageLevel == java.util.logging.Level.WARNING) {
 417                     logger.warning(fooMsg, thrown);
 418                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 419                 } else if (messageLevel == java.util.logging.Level.SEVERE) {
 420                     logger.severe(fooMsg, thrown);
 421                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 422                 }
 423             }
 424         }
 425 
 426         String format = "two params [{1} {2}]";
 427         Object arg1 = foo;
 428         Object arg2 = fooMsg;
 429         System.out.println("\tlogger.<level>(format, arg1, arg2)");
 430         for (java.util.logging.Level loggerLevel : julLevels) {
 431             setLevel(sink, loggerLevel);
 432             for (java.util.logging.Level messageLevel : julPlatformLevels) {
 433                 LogEvent expected =
 434                         LogEvent.of(
 435                             sequencer.get(),
 436                             loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
 437                             name, messageLevel, loggerBundle,
 438                             format, (Throwable)null, foo, fooMsg);
 439                 String desc2 = "logger." + messageLevel.toString().toLowerCase()
 440                         + "(format, foo, fooMsg): loggerLevel="
 441                         + loggerLevel+", messageLevel="+messageLevel;
 442                 if (messageLevel == java.util.logging.Level.FINEST) {
 443                     logger.finest(format, foo, fooMsg);
 444                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 445                 } else if (messageLevel == java.util.logging.Level.FINER) {
 446                     logger.finer(format, foo, fooMsg);
 447                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 448                 } else if (messageLevel == java.util.logging.Level.FINE) {
 449                     logger.fine(format, foo, fooMsg);
 450                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 451                 } else if (messageLevel == java.util.logging.Level.CONFIG) {
 452                     logger.config(format, foo, fooMsg);
 453                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 454                 } else if (messageLevel == java.util.logging.Level.INFO) {
 455                     logger.info(format, foo, fooMsg);
 456                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 457                 } else if (messageLevel == java.util.logging.Level.WARNING) {
 458                     logger.warning(format, foo, fooMsg);
 459                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 460                 } else if (messageLevel == java.util.logging.Level.SEVERE) {
 461                     logger.severe(format, foo, fooMsg);
 462                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 463                 }
 464             }
 465         }
 466 
 467     }
 468 
 469     final static class PermissionsBuilder {
 470         final Permissions perms;
 471         public PermissionsBuilder() {
 472             this(new Permissions());
 473         }
 474         public PermissionsBuilder(Permissions perms) {
 475             this.perms = perms;
 476         }
 477         public PermissionsBuilder add(Permission p) {
 478             perms.add(p);
 479             return this;
 480         }
 481         public PermissionsBuilder addAll(PermissionCollection col) {
 482             if (col != null) {
 483                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 484                     perms.add(e.nextElement());
 485                 }
 486             }
 487             return this;
 488         }
 489         public Permissions toPermissions() {
 490             final PermissionsBuilder builder = new PermissionsBuilder();
 491             builder.addAll(perms);
 492             return builder.perms;
 493         }
 494     }
 495 
 496     public static class SimplePolicy extends Policy {
 497 
 498         final Permissions permissions;
 499         final Permissions withControlPermissions;
 500         final Permissions allPermissions;
 501         final ThreadLocal<AtomicBoolean> allowAll;
 502         final ThreadLocal<AtomicBoolean> allowControl;
 503         public SimplePolicy(ThreadLocal<AtomicBoolean> allowAll,
 504                 ThreadLocal<AtomicBoolean> allowControl) {
 505             this.allowAll = allowAll;
 506             this.allowControl = allowControl;
 507             permissions = new Permissions();
 508 
 509             withControlPermissions = new Permissions();
 510             withControlPermissions.add(LoggerFinder.LOGGERFINDER_PERMISSION);
 511 
 512             // these are used for configuring the test itself...
 513             allPermissions = new Permissions();
 514             allPermissions.add(new java.security.AllPermission());
 515         }
 516 
 517         @Override
 518         public boolean implies(ProtectionDomain domain, Permission permission) {
 519             if (allowAll.get().get()) return allPermissions.implies(permission);
 520             if (allowControl.get().get()) return withControlPermissions.implies(permission);
 521             return permissions.implies(permission);
 522         }
 523 
 524         @Override
 525         public PermissionCollection getPermissions(CodeSource codesource) {
 526             return new PermissionsBuilder().addAll(
 527                     allowAll.get().get() ? allPermissions :
 528                     allowControl.get().get()
 529                     ? withControlPermissions : permissions).toPermissions();
 530         }
 531 
 532         @Override
 533         public PermissionCollection getPermissions(ProtectionDomain domain) {
 534             return new PermissionsBuilder().addAll(
 535                     allowAll.get().get() ? allPermissions :
 536                     allowControl.get().get()
 537                     ? withControlPermissions : permissions).toPermissions();
 538         }
 539     }
 540 }