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