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.AccessControlException;
  24 import java.security.AccessController;
  25 import java.security.CodeSource;
  26 import java.security.Permission;
  27 import java.security.PermissionCollection;
  28 import java.security.Permissions;
  29 import java.security.Policy;
  30 import java.security.PrivilegedAction;
  31 import java.security.ProtectionDomain;
  32 import java.util.Arrays;
  33 import java.util.Collections;
  34 import java.util.Enumeration;
  35 import java.util.HashMap;
  36 import java.util.Map;
  37 import java.util.Objects;
  38 import java.util.Queue;
  39 import java.util.ResourceBundle;
  40 import java.util.concurrent.ArrayBlockingQueue;
  41 import java.util.concurrent.ConcurrentHashMap;
  42 import java.util.concurrent.atomic.AtomicBoolean;
  43 import java.util.concurrent.atomic.AtomicLong;
  44 import java.util.function.Supplier;
  45 import sun.util.logging.PlatformLogger;
  46 import java.lang.System.LoggerFinder;
  47 import java.lang.System.Logger;
  48 import java.lang.System.Logger.Level;
  49 import java.util.stream.Stream;
  50 
  51 /**
  52  * @test
  53  * @bug     8140364
  54  * @summary JDK implementation specific unit test for JDK internal artifacts.
  55  *   Tests a naive implementation of System.Logger, and in particular
  56  *   the default mapping provided by PlatformLogger.Bridge.
  57  * @modules java.base/sun.util.logging java.base/jdk.internal.logger
  58  * @build CustomSystemClassLoader BaseLoggerBridgeTest
  59  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOSECURITY
  60  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOPERMISSIONS
  61  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest WITHPERMISSIONS
  62  * @author danielfuchs
  63  */
  64 public class BaseLoggerBridgeTest {
  65 
  66     static final RuntimePermission LOGGERFINDER_PERMISSION =
  67                 new RuntimePermission("loggerFinder");
  68     final static AtomicLong sequencer = new AtomicLong();
  69     final static boolean VERBOSE = false;
  70     // whether the implementation of Logger try to do a best
  71     // effort for logp... Our base logger finder stub doesn't
  72     // support logp, and thus the logp() implementation comes from
  73     // LoggerWrapper - which does a best effort.
  74     static final boolean BEST_EFFORT_FOR_LOGP = true;
  75     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
  76         @Override
  77         protected AtomicBoolean initialValue() {
  78             return  new AtomicBoolean(false);
  79         }
  80     };
  81     static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
  82         @Override
  83         protected AtomicBoolean initialValue() {
  84             return  new AtomicBoolean(false);
  85         }
  86     };
  87     static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
  88         @Override
  89         protected AtomicBoolean initialValue() {
  90             return  new AtomicBoolean(false);
  91         }
  92     };
  93 
  94     static final Class<?> providerClass;
  95     static {
  96         try {
  97             providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerBridgeTest$BaseLoggerFinder");
  98         } catch (ClassNotFoundException ex) {
  99             throw new ExceptionInInitializerError(ex);
 100         }
 101     }
 102 
 103     static final sun.util.logging.PlatformLogger.Level[] julLevels = {
 104         sun.util.logging.PlatformLogger.Level.ALL,
 105         sun.util.logging.PlatformLogger.Level.FINEST,
 106         sun.util.logging.PlatformLogger.Level.FINER,
 107         sun.util.logging.PlatformLogger.Level.FINE,
 108         sun.util.logging.PlatformLogger.Level.CONFIG,
 109         sun.util.logging.PlatformLogger.Level.INFO,
 110         sun.util.logging.PlatformLogger.Level.WARNING,
 111         sun.util.logging.PlatformLogger.Level.SEVERE,
 112         sun.util.logging.PlatformLogger.Level.OFF,
 113     };
 114 
 115     static final Level[] mappedLevels = {
 116         Level.ALL,     // ALL
 117         Level.TRACE,   // FINEST
 118         Level.TRACE,   // FINER
 119         Level.DEBUG,   // FINE
 120         Level.DEBUG,   // CONFIG
 121         Level.INFO,    // INFO
 122         Level.WARNING, // WARNING
 123         Level.ERROR,   // SEVERE
 124         Level.OFF,     // OFF
 125     };
 126 
 127     final static Map<sun.util.logging.PlatformLogger.Level, Level> julToSpiMap;
 128     static {
 129         Map<sun.util.logging.PlatformLogger.Level, Level> map = new HashMap<>();
 130         if (mappedLevels.length != julLevels.length) {
 131             throw new ExceptionInInitializerError("Array lengths differ"
 132                 + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
 133                 + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
 134         }
 135         for (int i=0; i<julLevels.length; i++) {
 136             map.put(julLevels[i], mappedLevels[i]);
 137         }
 138         julToSpiMap = Collections.unmodifiableMap(map);
 139     }
 140 
 141     public static class MyBundle extends ResourceBundle {
 142 
 143         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
 144 
 145         @Override
 146         protected Object handleGetObject(String key) {
 147             if (key.contains(" (translated)")) {
 148                 throw new RuntimeException("Unexpected key: " + key);
 149             }
 150             return map.computeIfAbsent(key, k -> k + " (translated)");
 151         }
 152 
 153         @Override
 154         public Enumeration<String> getKeys() {
 155             return Collections.enumeration(map.keySet());
 156         }
 157 
 158     }
 159     public static class MyLoggerBundle extends MyBundle {
 160 
 161     }
 162 
 163     public static interface TestLoggerFinder {
 164         final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
 165         final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
 166         public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
 167 
 168         public static final class LogEvent implements Cloneable {
 169 
 170             public LogEvent() {
 171                 this(sequencer.getAndIncrement());
 172             }
 173 
 174             LogEvent(long sequenceNumber) {
 175                 this.sequenceNumber = sequenceNumber;
 176             }
 177 
 178             boolean callSupplier = false;
 179             long sequenceNumber;
 180             boolean isLoggable;
 181             String loggerName;
 182             Level level;
 183             ResourceBundle bundle;
 184             Throwable thrown;
 185             Object[] args;
 186             Supplier<String> supplier;
 187             String msg;
 188 
 189             Object[] toArray(boolean callSupplier) {
 190                 return new Object[] {
 191                     sequenceNumber,
 192                     isLoggable,
 193                     loggerName,
 194                     level,
 195                     bundle,
 196                     thrown,
 197                     args,
 198                     callSupplier && supplier != null ? supplier.get() : supplier,
 199                     msg,
 200                 };
 201             }
 202 
 203             boolean callSupplier(Object obj) {
 204                 return callSupplier || ((LogEvent)obj).callSupplier;
 205             }
 206 
 207             @Override
 208             public String toString() {
 209                 return Arrays.deepToString(toArray(false));
 210             }
 211 
 212             @Override
 213             public boolean equals(Object obj) {
 214                 return obj instanceof LogEvent
 215                         && Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj)));
 216             }
 217 
 218             @Override
 219             public int hashCode() {
 220                 return Objects.hash(toArray(true));
 221             }
 222 
 223             public LogEvent cloneWith(long sequenceNumber)
 224                     throws CloneNotSupportedException {
 225                 LogEvent cloned = (LogEvent)super.clone();
 226                 cloned.sequenceNumber = sequenceNumber;
 227                 return cloned;
 228             }
 229 
 230             public static LogEvent of(boolean isLoggable, String name,
 231                     Level level, ResourceBundle bundle,
 232                     String key, Throwable thrown) {
 233                 LogEvent evt = new LogEvent();
 234                 evt.isLoggable = isLoggable;
 235                 evt.loggerName = name;
 236                 evt.level = level;
 237                 evt.args = null;
 238                 evt.bundle = bundle;
 239                 evt.thrown = thrown;
 240                 evt.supplier = null;
 241                 evt.msg = key;
 242                 return evt;
 243             }
 244 
 245             public static LogEvent of(boolean isLoggable, String name,
 246                     Level level, Throwable thrown, Supplier<String> supplier) {
 247                 LogEvent evt = new LogEvent();
 248                 evt.isLoggable = isLoggable;
 249                 evt.loggerName = name;
 250                 evt.level = level;
 251                 evt.args = null;
 252                 evt.bundle = null;
 253                 evt.thrown = thrown;
 254                 evt.supplier = supplier;
 255                 evt.msg = null;
 256                 return evt;
 257             }
 258 
 259             public static LogEvent of(boolean isLoggable, String name,
 260                     Level level, ResourceBundle bundle,
 261                     String key, Object... params) {
 262                 LogEvent evt = new LogEvent();
 263                 evt.isLoggable = isLoggable;
 264                 evt.loggerName = name;
 265                 evt.level = level;
 266                 evt.args = params;
 267                 evt.bundle = bundle;
 268                 evt.thrown = null;
 269                 evt.supplier = null;
 270                 evt.msg = key;
 271                 return evt;
 272             }
 273 
 274             public static LogEvent of(long sequenceNumber,
 275                     boolean isLoggable, String name,
 276                     Level level, ResourceBundle bundle,
 277                     String key, Supplier<String> supplier,
 278                     Throwable thrown, Object... params) {
 279                 LogEvent evt = new LogEvent(sequenceNumber);
 280                 evt.loggerName = name;
 281                 evt.level = level;
 282                 evt.args = params;
 283                 evt.bundle = bundle;
 284                 evt.thrown = thrown;
 285                 evt.supplier = supplier;
 286                 evt.msg = key;
 287                 evt.isLoggable = isLoggable;
 288                 return evt;
 289             }
 290 
 291             public static LogEvent ofp(boolean callSupplier, LogEvent evt) {
 292                 evt.callSupplier = callSupplier;
 293                 return evt;
 294             }
 295         }
 296 
 297         public class LoggerImpl implements Logger {
 298             private final String name;
 299             private Level level = Level.INFO;
 300 
 301             public LoggerImpl(String name) {
 302                 this.name = name;
 303             }
 304 
 305             @Override
 306             public String getName() {
 307                 return name;
 308             }
 309 
 310             @Override
 311             public boolean isLoggable(Level level) {
 312                 return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
 313             }
 314 
 315             @Override
 316             public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
 317                 log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
 318             }
 319 
 320             @Override
 321             public void log(Level level, ResourceBundle bundle, String format, Object... params) {
 322                 log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
 323             }
 324 
 325             void log(LogEvent event) {
 326                 eventQueue.add(event);
 327             }
 328 
 329             @Override
 330             public void log(Level level, Supplier<String> msgSupplier) {
 331                 log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
 332             }
 333 
 334             @Override
 335             public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
 336                 log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
 337             }
 338 
 339 
 340 
 341         }
 342 
 343         public Logger getLogger(String name, Module caller);
 344         public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
 345     }
 346 
 347     public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
 348         static final RuntimePermission LOGGERFINDER_PERMISSION =
 349                 new RuntimePermission("loggerFinder");
 350         @Override
 351         public Logger getLogger(String name, Module caller) {
 352             SecurityManager sm = System.getSecurityManager();
 353             if (sm != null) {
 354                 sm.checkPermission(LOGGERFINDER_PERMISSION);
 355             }
 356             PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
 357             ClassLoader callerLoader = AccessController.doPrivileged(pa);
 358             if (callerLoader == null) {
 359                 return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
 360             } else {
 361                 return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
 362             }
 363         }
 364     }
 365 
 366     static PlatformLogger.Bridge convert(Logger logger) {
 367         boolean old = allowAll.get().get();
 368         allowAccess.get().set(true);
 369         try {
 370             return PlatformLogger.Bridge.convert(logger);
 371         } finally {
 372             allowAccess.get().set(old);
 373         }
 374     }
 375 
 376     static Logger getLogger(String name, Module caller) {
 377         boolean old = allowAll.get().get();
 378         allowAccess.get().set(true);
 379         try {
 380             return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
 381         } finally {
 382             allowAccess.get().set(old);
 383         }
 384     }
 385 
 386     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
 387 
 388     static void setSecurityManager() {
 389         if (System.getSecurityManager() == null) {
 390             // Ugly test hack: preload the resources needed by String.format
 391             //   We need to do that before setting the security manager
 392             //   because our implementation of CustomSystemClassLoader
 393             //   doesn't have the required permission.
 394             System.out.println(String.format("debug: %s", "Setting security manager"));
 395             Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
 396             System.setSecurityManager(new SecurityManager());
 397         }
 398     }
 399 
 400     public static void main(String[] args) {
 401         if (args.length == 0)
 402             args = new String[] {
 403                 "NOSECURITY",
 404                 "NOPERMISSIONS",
 405                 "WITHPERMISSIONS"
 406             };
 407 
 408 
 409         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 410             TestLoggerFinder provider;
 411             switch (testCase) {
 412                 case NOSECURITY:
 413                     System.out.println("\n*** Without Security Manager\n");
 414                     provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 415                     test(provider, true);
 416                     System.out.println("Tetscase count: " + sequencer.get());
 417                     break;
 418                 case NOPERMISSIONS:
 419                     System.out.println("\n*** With Security Manager, without permissions\n");
 420                     setSecurityManager();
 421                     try {
 422                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 423                         throw new RuntimeException("Expected exception not raised");
 424                     } catch (AccessControlException x) {
 425                         if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
 426                             throw new RuntimeException("Unexpected permission check", x);
 427                         }
 428                         final boolean control = allowControl.get().get();
 429                         try {
 430                             allowControl.get().set(true);
 431                             provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 432                         } finally {
 433                             allowControl.get().set(control);
 434                         }
 435                     }
 436                     test(provider, false);
 437                     System.out.println("Tetscase count: " + sequencer.get());
 438                     break;
 439                 case WITHPERMISSIONS:
 440                     System.out.println("\n*** With Security Manager, with control permission\n");
 441                     setSecurityManager();
 442                     final boolean control = allowControl.get().get();
 443                     try {
 444                         allowControl.get().set(true);
 445                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 446                         test(provider, true);
 447                     } finally {
 448                         allowControl.get().set(control);
 449                     }
 450                     break;
 451                 default:
 452                     throw new RuntimeException("Unknown test case: " + testCase);
 453             }
 454         });
 455         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 456     }
 457 
 458     public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
 459 
 460         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 461         final Map<Object, String> loggerDescMap = new HashMap<>();
 462 
 463 
 464         TestLoggerFinder.LoggerImpl appSink = null;
 465         try {
 466             appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
 467             if (!hasRequiredPermissions) {
 468                 throw new RuntimeException("Managed to obtain a system logger without permission");
 469             }
 470         } catch (AccessControlException acx) {
 471             if (hasRequiredPermissions) {
 472                 throw new RuntimeException("Unexpected security exception: ", acx);
 473             }
 474             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 475                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 476             }
 477             System.out.println("Got expected exception for logger: " + acx);
 478             boolean old = allowControl.get().get();
 479             allowControl.get().set(true);
 480             try {
 481                 appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
 482             } finally {
 483                 allowControl.get().set(old);
 484             }
 485         }
 486 
 487 
 488         TestLoggerFinder.LoggerImpl sysSink = null;
 489         try {
 490             sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));
 491             if (!hasRequiredPermissions) {
 492                 throw new RuntimeException("Managed to obtain a system logger without permission");
 493             }
 494         } catch (AccessControlException acx) {
 495             if (hasRequiredPermissions) {
 496                 throw new RuntimeException("Unexpected security exception: ", acx);
 497             }
 498             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 499                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 500             }
 501             System.out.println("Got expected exception for system logger: " + acx);
 502         }
 503         if (hasRequiredPermissions && appSink == sysSink) {
 504             throw new RuntimeException("identical loggers");
 505         }
 506 
 507         if (provider.system.contains(appSink)) {
 508             throw new RuntimeException("app logger in system map");
 509         }
 510         if (!provider.user.contains(appSink)) {
 511             throw new RuntimeException("app logger not in appplication map");
 512         }
 513         if (hasRequiredPermissions && provider.user.contains(sysSink)) {
 514             throw new RuntimeException("sys logger in appplication map");
 515         }
 516         if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
 517             throw new RuntimeException("sys logger not in system map");
 518         }
 519 
 520         Logger appLogger1 = System.getLogger("foo");
 521         loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
 522         PlatformLogger.Bridge bridge = convert(appLogger1);
 523         loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
 524         testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
 525 
 526         Logger sysLogger1 = null;
 527         try {
 528             sysLogger1 = getLogger("foo", Thread.class.getModule());
 529             loggerDescMap.put(sysLogger1,
 530                     "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class.getModule())");
 531 
 532             if (!hasRequiredPermissions) {
 533                 // check that the provider would have thrown an exception
 534                 provider.getLogger("foo", Thread.class.getModule());
 535                 throw new RuntimeException("Managed to obtain a system logger without permission");
 536             }
 537         } catch (AccessControlException acx) {
 538             if (hasRequiredPermissions) {
 539                 throw new RuntimeException("Unexpected security exception: ", acx);
 540             }
 541             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 542                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 543             }
 544             System.out.println("Got expected exception for system logger: " + acx);
 545         }
 546 
 547         if (hasRequiredPermissions) {
 548             // if we don't have permissions sysSink will be null.
 549             testLogger(provider, loggerDescMap, "foo", null,
 550                 PlatformLogger.Bridge.convert(sysLogger1), sysSink);
 551         }
 552 
 553         Logger appLogger2 =
 554                 System.getLogger("foo", loggerBundle);
 555         loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
 556 
 557         if (appLogger2 == appLogger1) {
 558             throw new RuntimeException("identical loggers");
 559         }
 560 
 561         if (provider.system.contains(appLogger2)) {
 562             throw new RuntimeException("localized app logger in system map");
 563         }
 564         if (provider.user.contains(appLogger2)) {
 565             throw new RuntimeException("localized app logger  in appplication map");
 566         }
 567 
 568         testLogger(provider, loggerDescMap, "foo", loggerBundle,
 569                 PlatformLogger.Bridge.convert(appLogger2), appSink);
 570 
 571         Logger sysLogger2 = null;
 572         try {
 573             sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
 574             loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
 575             if (!hasRequiredPermissions) {
 576                 throw new RuntimeException("Managed to obtain a system logger without permission");
 577             }
 578         } catch (AccessControlException acx) {
 579             if (hasRequiredPermissions) {
 580                 throw new RuntimeException("Unexpected security exception: ", acx);
 581             }
 582             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 583                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 584             }
 585             System.out.println("Got expected exception for localized system logger: " + acx);
 586         }
 587         if (hasRequiredPermissions && appLogger2 == sysLogger2) {
 588             throw new RuntimeException("identical loggers");
 589         }
 590         if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
 591             throw new RuntimeException("identical loggers");
 592         }
 593         if (hasRequiredPermissions && provider.user.contains(sysLogger2)) {
 594             throw new RuntimeException("localized sys logger in appplication map");
 595         }
 596         if (hasRequiredPermissions && provider.system.contains(sysLogger2)) {
 597             throw new RuntimeException("localized sys logger not in system map");
 598         }
 599 
 600         if (hasRequiredPermissions) {
 601             // if we don't have permissions sysSink will be null.
 602             testLogger(provider, loggerDescMap, "foo", loggerBundle,
 603                 PlatformLogger.Bridge.convert(sysLogger2), sysSink);
 604         }
 605 
 606     }
 607 
 608     public static class Foo {
 609 
 610     }
 611 
 612     static void verbose(String msg) {
 613        if (VERBOSE) {
 614            System.out.println(msg);
 615        }
 616     }
 617 
 618     static void checkLogEvent(TestLoggerFinder provider, String desc,
 619             TestLoggerFinder.LogEvent expected) {
 620         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 621         if (!Objects.equals(expected, actual)) {
 622             throw new RuntimeException("mismatch for " + desc
 623                     + "\n\texpected=" + expected
 624                     + "\n\t  actual=" + actual);
 625         } else {
 626             verbose("Got expected results for "
 627                     + desc + "\n\t" + expected);
 628         }
 629     }
 630 
 631     static void checkLogEvent(TestLoggerFinder provider, String desc,
 632             TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
 633         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 634         if (actual == null && !expectNotNull) return;
 635         if (actual != null && !expectNotNull) {
 636             throw new RuntimeException("Unexpected log event found for " + desc
 637                 + "\n\tgot: " + actual);
 638         }
 639         if (!expected.equals(actual)) {
 640             throw new RuntimeException("mismatch for " + desc
 641                     + "\n\texpected=" + expected
 642                     + "\n\t  actual=" + actual);
 643         } else {
 644             verbose("Got expected results for "
 645                     + desc + "\n\t" + expected);
 646         }
 647     }
 648 
 649         static Supplier<String> logpMessage(ResourceBundle bundle,
 650                 String className, String methodName, Supplier<String> msg) {
 651             if (BEST_EFFORT_FOR_LOGP && bundle == null
 652                     && (className != null || methodName != null)) {
 653                 final String cName = className == null ? "" :  className;
 654                 final String mName = methodName == null ? "" : methodName;
 655                 return () -> String.format("[%s %s] %s", cName, mName, msg.get());
 656             } else {
 657                 return msg;
 658             }
 659         }
 660 
 661         static String logpMessage(ResourceBundle bundle,
 662                 String className, String methodName, String msg) {
 663             if (BEST_EFFORT_FOR_LOGP && bundle == null
 664                     && (className != null || methodName != null)) {
 665                 final String cName = className == null ? "" :  className;
 666                 final String mName = methodName == null ? "" : methodName;
 667                 return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
 668             } else {
 669                 return msg;
 670             }
 671         }
 672 
 673     // Calls the methods defined on LogProducer and verify the
 674     // parameters received by the underlying TestLoggerFinder.LoggerImpl
 675     // logger.
 676     private static void testLogger(TestLoggerFinder provider,
 677             Map<Object, String> loggerDescMap,
 678             String name,
 679             ResourceBundle loggerBundle,
 680             PlatformLogger.Bridge logger,
 681             TestLoggerFinder.LoggerImpl sink) {
 682 
 683         if (loggerDescMap.get(logger) == null) {
 684             throw new RuntimeException("Test bug: Missing description");
 685         }
 686         System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
 687 
 688         Foo foo = new Foo();
 689         String fooMsg = foo.toString();
 690         System.out.println("\tlogger.log(messageLevel, fooMsg)");
 691         System.out.println("\tlogger.<level>(fooMsg)");
 692         for (Level loggerLevel : Level.values()) {
 693             sink.level = loggerLevel;
 694             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 695                 String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
 696                         + loggerLevel+", messageLevel="+messageLevel;
 697                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 698                 TestLoggerFinder.LogEvent expected =
 699                         TestLoggerFinder.LogEvent.of(
 700                             sequencer.get(),
 701                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 702                             name, expectedMessageLevel, loggerBundle,
 703                             fooMsg, null, (Throwable)null, (Object[])null);
 704                 logger.log(messageLevel, fooMsg);
 705                 checkLogEvent(provider, desc, expected);
 706             }
 707         }
 708 
 709         Supplier<String> supplier = new Supplier<String>() {
 710             @Override
 711             public String get() {
 712                 return this.toString();
 713             }
 714         };
 715         System.out.println("\tlogger.log(messageLevel, supplier)");
 716         System.out.println("\tlogger.<level>(supplier)");
 717         for (Level loggerLevel : Level.values()) {
 718             sink.level = loggerLevel;
 719             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 720                 String desc = "logger.log(messageLevel, supplier): loggerLevel="
 721                         + loggerLevel+", messageLevel="+messageLevel;
 722                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 723                 TestLoggerFinder.LogEvent expected =
 724                         TestLoggerFinder.LogEvent.of(
 725                             sequencer.get(),
 726                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 727                             name, expectedMessageLevel, (ResourceBundle) null,
 728                             null, supplier, (Throwable)null, (Object[])null);
 729                 logger.log(messageLevel, supplier);
 730                 checkLogEvent(provider, desc, expected);
 731             }
 732         }
 733 
 734         String format = "two params [{1} {2}]";
 735         Object arg1 = foo;
 736         Object arg2 = fooMsg;
 737         System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
 738         for (Level loggerLevel : Level.values()) {
 739             sink.level = loggerLevel;
 740             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 741                 String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
 742                         + loggerLevel+", messageLevel="+messageLevel;
 743                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 744                 TestLoggerFinder.LogEvent expected =
 745                         TestLoggerFinder.LogEvent.of(
 746                             sequencer.get(),
 747                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 748                             name, expectedMessageLevel, loggerBundle,
 749                             format, null, (Throwable)null, arg1, arg2);
 750                 logger.log(messageLevel, format, arg1, arg2);
 751                 checkLogEvent(provider, desc, expected);
 752             }
 753         }
 754 
 755         Throwable thrown = new Exception("OK: log me!");
 756         System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
 757         for (Level loggerLevel : Level.values()) {
 758             sink.level = loggerLevel;
 759             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 760                 String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
 761                         + loggerLevel+", messageLevel="+messageLevel;
 762                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 763                 TestLoggerFinder.LogEvent expected =
 764                         TestLoggerFinder.LogEvent.of(
 765                             sequencer.get(),
 766                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 767                             name, expectedMessageLevel, loggerBundle,
 768                             fooMsg, null, thrown, (Object[])null);
 769                 logger.log(messageLevel, fooMsg, thrown);
 770                 checkLogEvent(provider, desc, expected);
 771             }
 772         }
 773 
 774         System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
 775         for (Level loggerLevel : Level.values()) {
 776             sink.level = loggerLevel;
 777             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 778                 String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
 779                         + loggerLevel+", messageLevel="+messageLevel;
 780                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 781                 TestLoggerFinder.LogEvent expected =
 782                         TestLoggerFinder.LogEvent.of(
 783                             sequencer.get(),
 784                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 785                             name, expectedMessageLevel, (ResourceBundle)null,
 786                             null, supplier, thrown, (Object[])null);
 787                 logger.log(messageLevel, thrown, supplier);
 788                 checkLogEvent(provider, desc, expected);
 789             }
 790         }
 791 
 792         String sourceClass = "blah.Blah";
 793         String sourceMethod = "blih";
 794         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
 795         for (Level loggerLevel : Level.values()) {
 796             sink.level = loggerLevel;
 797             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 798                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
 799                         + loggerLevel+", messageLevel="+messageLevel;
 800                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 801                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 802                 TestLoggerFinder.LogEvent expected =
 803                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
 804                         TestLoggerFinder.LogEvent.of(
 805                             sequencer.get(),
 806                             isLoggable,
 807                             name, expectedMessageLevel, loggerBundle,
 808                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
 809                             null, (Throwable)null, (Object[]) null) : null;
 810                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
 811                 checkLogEvent(provider, desc, expected);
 812             }
 813         }
 814 
 815         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
 816         for (Level loggerLevel : Level.values()) {
 817             sink.level = loggerLevel;
 818             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 819                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
 820                         + loggerLevel+", messageLevel="+messageLevel;
 821                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 822                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 823                 TestLoggerFinder.LogEvent expected = isLoggable ?
 824                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
 825                         TestLoggerFinder.LogEvent.of(
 826                             sequencer.get(),
 827                             isLoggable,
 828                             name, expectedMessageLevel, null, null,
 829                             logpMessage(null, sourceClass, sourceMethod, supplier),
 830                             (Throwable)null, (Object[]) null)) : null;
 831                 logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
 832                 checkLogEvent(provider, desc, expected);
 833             }
 834         }
 835 
 836         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
 837         for (Level loggerLevel : Level.values()) {
 838             sink.level = loggerLevel;
 839             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 840                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
 841                         + loggerLevel+", messageLevel="+messageLevel;
 842                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 843                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 844                 TestLoggerFinder.LogEvent expected =
 845                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
 846                         TestLoggerFinder.LogEvent.of(
 847                             sequencer.get(),
 848                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 849                             name, expectedMessageLevel, loggerBundle,
 850                             logpMessage(loggerBundle, sourceClass, sourceMethod, format),
 851                             null, (Throwable)null, arg1, arg2) : null;
 852                 logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
 853                 checkLogEvent(provider, desc, expected);
 854             }
 855         }
 856 
 857         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
 858         for (Level loggerLevel : Level.values()) {
 859             sink.level = loggerLevel;
 860             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 861                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
 862                         + loggerLevel+", messageLevel="+messageLevel;
 863                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 864                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 865                 TestLoggerFinder.LogEvent expected =
 866                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
 867                         TestLoggerFinder.LogEvent.of(
 868                             sequencer.get(),
 869                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 870                             name, expectedMessageLevel, loggerBundle,
 871                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
 872                             null, thrown, (Object[])null) : null;
 873                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
 874                 checkLogEvent(provider, desc, expected);
 875             }
 876         }
 877 
 878         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
 879         for (Level loggerLevel : Level.values()) {
 880             sink.level = loggerLevel;
 881             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 882                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
 883                         + loggerLevel+", messageLevel="+messageLevel;
 884                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 885                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 886                 TestLoggerFinder.LogEvent expected = isLoggable ?
 887                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
 888                         TestLoggerFinder.LogEvent.of(
 889                             sequencer.get(),
 890                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 891                             name, expectedMessageLevel, null, null,
 892                             logpMessage(null, sourceClass, sourceMethod, supplier),
 893                             thrown, (Object[])null)) : null;
 894                 logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
 895                 checkLogEvent(provider, desc, expected);
 896             }
 897         }
 898 
 899         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 900         System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
 901         for (Level loggerLevel : Level.values()) {
 902             sink.level = loggerLevel;
 903             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 904                 String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
 905                         + loggerLevel+", messageLevel="+messageLevel;
 906                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 907                 TestLoggerFinder.LogEvent expected =
 908                         TestLoggerFinder.LogEvent.of(
 909                             sequencer.get(),
 910                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 911                             name, expectedMessageLevel, bundle,
 912                             format, null, (Throwable)null, arg1, arg2);
 913                 logger.logrb(messageLevel, bundle, format, arg1, arg2);
 914                 checkLogEvent(provider, desc, expected);
 915             }
 916         }
 917 
 918         System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
 919         for (Level loggerLevel : Level.values()) {
 920             sink.level = loggerLevel;
 921             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 922                 String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
 923                         + loggerLevel+", messageLevel="+messageLevel;
 924                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 925                 TestLoggerFinder.LogEvent expected =
 926                         TestLoggerFinder.LogEvent.of(
 927                             sequencer.get(),
 928                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 929                             name, expectedMessageLevel, bundle,
 930                             fooMsg, null, thrown, (Object[])null);
 931                 logger.logrb(messageLevel, bundle, fooMsg, thrown);
 932                 checkLogEvent(provider, desc, expected);
 933             }
 934         }
 935 
 936         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
 937         for (Level loggerLevel : Level.values()) {
 938             sink.level = loggerLevel;
 939             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 940                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
 941                         + loggerLevel+", messageLevel="+messageLevel;
 942                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 943                 TestLoggerFinder.LogEvent expected =
 944                         TestLoggerFinder.LogEvent.of(
 945                             sequencer.get(),
 946                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 947                             name, expectedMessageLevel, bundle,
 948                             format, null, (Throwable)null, arg1, arg2);
 949                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
 950                 checkLogEvent(provider, desc, expected);
 951             }
 952         }
 953 
 954         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
 955         for (Level loggerLevel : Level.values()) {
 956             sink.level = loggerLevel;
 957             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 958                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
 959                         + loggerLevel+", messageLevel="+messageLevel;
 960                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 961                 TestLoggerFinder.LogEvent expected =
 962                         TestLoggerFinder.LogEvent.of(
 963                             sequencer.get(),
 964                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 965                             name, expectedMessageLevel, bundle,
 966                             fooMsg, null, thrown, (Object[])null);
 967                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
 968                 checkLogEvent(provider, desc, expected);
 969             }
 970         }
 971     }
 972 
 973     final static class PermissionsBuilder {
 974         final Permissions perms;
 975         public PermissionsBuilder() {
 976             this(new Permissions());
 977         }
 978         public PermissionsBuilder(Permissions perms) {
 979             this.perms = perms;
 980         }
 981         public PermissionsBuilder add(Permission p) {
 982             perms.add(p);
 983             return this;
 984         }
 985         public PermissionsBuilder addAll(PermissionCollection col) {
 986             if (col != null) {
 987                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 988                     perms.add(e.nextElement());
 989                 }
 990             }
 991             return this;
 992         }
 993         public Permissions toPermissions() {
 994             final PermissionsBuilder builder = new PermissionsBuilder();
 995             builder.addAll(perms);
 996             return builder.perms;
 997         }
 998     }
 999 
1000     public static class SimplePolicy extends Policy {
1001         final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
1002         final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
1003         final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
1004 
1005         final Permissions permissions;
1006         final Permissions allPermissions;
1007         final ThreadLocal<AtomicBoolean> allowControl;
1008         final ThreadLocal<AtomicBoolean> allowAccess;
1009         final ThreadLocal<AtomicBoolean> allowAll;
1010         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
1011                 ThreadLocal<AtomicBoolean> allowAccess,
1012                 ThreadLocal<AtomicBoolean> allowAll) {
1013             this.allowControl = allowControl;
1014             this.allowAccess = allowAccess;
1015             this.allowAll = allowAll;
1016             permissions = new Permissions();
1017             allPermissions = new PermissionsBuilder()
1018                     .add(new java.security.AllPermission())
1019                     .toPermissions();
1020         }
1021 
1022         Permissions getPermissions() {
1023             if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
1024                 PermissionsBuilder builder =  new PermissionsBuilder()
1025                         .addAll(permissions);
1026                 if (allowControl.get().get()) {
1027                     builder.add(CONTROL);
1028                 }
1029                 if (allowAccess.get().get()) {
1030                     builder.add(ACCESS_LOGGER);
1031                     builder.add(ACCESS_LOGGING);
1032                 }
1033                 if (allowAll.get().get()) {
1034                     builder.addAll(allPermissions);
1035                 }
1036                 return builder.toPermissions();
1037             }
1038             return permissions;
1039         }
1040 
1041         @Override
1042         public boolean implies(ProtectionDomain domain, Permission permission) {
1043             return getPermissions().implies(permission);
1044         }
1045 
1046         @Override
1047         public PermissionCollection getPermissions(CodeSource codesource) {
1048             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1049         }
1050 
1051         @Override
1052         public PermissionCollection getPermissions(ProtectionDomain domain) {
1053             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1054         }
1055     }
1056 }