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.CodeSource;
  25 import java.security.Permission;
  26 import java.security.PermissionCollection;
  27 import java.security.Permissions;
  28 import java.security.Policy;
  29 import java.security.ProtectionDomain;
  30 import java.util.Arrays;
  31 import java.util.Collections;
  32 import java.util.Enumeration;
  33 import java.util.HashMap;
  34 import java.util.Map;
  35 import java.util.Objects;
  36 import java.util.Queue;
  37 import java.util.ResourceBundle;
  38 import java.util.concurrent.ArrayBlockingQueue;
  39 import java.util.concurrent.ConcurrentHashMap;
  40 import java.util.concurrent.atomic.AtomicBoolean;
  41 import java.util.concurrent.atomic.AtomicLong;
  42 import java.util.function.Supplier;
  43 import java.util.logging.Handler;
  44 import java.util.logging.LogManager;
  45 import java.util.logging.LogRecord;
  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     8046565
  54  * @summary Tests the default implementation of System.Logger, when
  55  *          JUL is the default backend.
  56  * @build AccessSystemLogger DefaultLoggerFinderTest
  57  * @run  driver AccessSystemLogger
  58  * @run  main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOSECURITY
  59  * @run  main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOPERMISSIONS
  60  * @run  main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest WITHPERMISSIONS
  61  * @author danielfuchs
  62  */
  63 public class DefaultLoggerFinderTest {
  64 
  65     final static AtomicLong sequencer = new AtomicLong();
  66     final static boolean VERBOSE = false;
  67     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
  68         @Override
  69         protected AtomicBoolean initialValue() {
  70             return  new AtomicBoolean(false);
  71         }
  72     };
  73     static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
  74         @Override
  75         protected AtomicBoolean initialValue() {
  76             return  new AtomicBoolean(false);
  77         }
  78     };
  79 
  80     static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
  81 
  82     public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
  83 
  84     public static final class LogEvent {
  85 
  86         public LogEvent() {
  87             this(sequencer.getAndIncrement());
  88         }
  89 
  90         LogEvent(long sequenceNumber) {
  91             this.sequenceNumber = sequenceNumber;
  92         }
  93 
  94         long sequenceNumber;
  95         boolean isLoggable;
  96         String loggerName;
  97         java.util.logging.Level level;
  98         ResourceBundle bundle;
  99         Throwable thrown;
 100         Object[] args;
 101         String msg;
 102         String className;
 103         String methodName;
 104 
 105         Object[] toArray() {
 106             return new Object[] {
 107                 sequenceNumber,
 108                 isLoggable,
 109                 loggerName,
 110                 level,
 111                 bundle,
 112                 thrown,
 113                 args,
 114                 msg,
 115                 className,
 116                 methodName,
 117             };
 118         }
 119 
 120         @Override
 121         public String toString() {
 122             return Arrays.deepToString(toArray());
 123         }
 124 
 125         @Override
 126         public boolean equals(Object obj) {
 127             return obj instanceof LogEvent
 128                     && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
 129         }
 130 
 131         @Override
 132         public int hashCode() {
 133             return Objects.hash(toArray());
 134         }
 135         public static LogEvent of(long sequenceNumber,
 136                 boolean isLoggable, String name,
 137                 java.util.logging.Level level, ResourceBundle bundle,
 138                 String key, Throwable thrown, Object... params) {
 139             return LogEvent.of(sequenceNumber, isLoggable, name,
 140                     DefaultLoggerFinderTest.class.getName(),
 141                     "testLogger", level, bundle, key,
 142                     thrown, params);
 143         }
 144         public static LogEvent of(long sequenceNumber,
 145                 boolean isLoggable, String name,
 146                 String className, String methodName,
 147                 java.util.logging.Level level, ResourceBundle bundle,
 148                 String key, Throwable thrown, Object... params) {
 149             LogEvent evt = new LogEvent(sequenceNumber);
 150             evt.loggerName = name;
 151             evt.level = level;
 152             evt.args = params;
 153             evt.bundle = bundle;
 154             evt.thrown = thrown;
 155             evt.msg = key;
 156             evt.isLoggable = isLoggable;
 157             evt.className = className;
 158             evt.methodName = methodName;
 159             return evt;
 160         }
 161 
 162     }
 163 
 164     static java.util.logging.Level mapToJul(Level level) {
 165         switch (level) {
 166             case ALL: return java.util.logging.Level.ALL;
 167             case TRACE: return java.util.logging.Level.FINER;
 168             case DEBUG: return java.util.logging.Level.FINE;
 169             case INFO: return java.util.logging.Level.INFO;
 170             case WARNING: return java.util.logging.Level.WARNING;
 171             case ERROR: return java.util.logging.Level.SEVERE;
 172             case OFF: return java.util.logging.Level.OFF;
 173         }
 174         throw new InternalError("No such level: " + level);
 175     }
 176 
 177     static final java.util.logging.Level[] julLevels = {
 178         java.util.logging.Level.ALL,
 179         new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
 180         java.util.logging.Level.FINEST,
 181         new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
 182         java.util.logging.Level.FINER,
 183         new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
 184         java.util.logging.Level.FINE,
 185         new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
 186         java.util.logging.Level.CONFIG,
 187         new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
 188         java.util.logging.Level.INFO,
 189         new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
 190         java.util.logging.Level.WARNING,
 191         new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
 192         java.util.logging.Level.SEVERE,
 193         new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
 194         java.util.logging.Level.OFF,
 195     };
 196 
 197     static final Level[] mappedLevels = {
 198         Level.ALL,     // ALL
 199         Level.DEBUG,   // FINER_THAN_FINEST
 200         Level.DEBUG,   // FINEST
 201         Level.DEBUG,   // FINER_THAN_FINER
 202         Level.TRACE,   // FINER
 203         Level.TRACE,   // FINER_THAN_FINE
 204         Level.DEBUG,   // FINE
 205         Level.DEBUG,   // FINER_THAN_CONFIG
 206         Level.DEBUG,   // CONFIG
 207         Level.DEBUG,   // FINER_THAN_INFO
 208         Level.INFO,    // INFO
 209         Level.INFO,    // FINER_THAN_WARNING
 210         Level.WARNING, // WARNING
 211         Level.WARNING, // FINER_THAN_SEVERE
 212         Level.ERROR,   // SEVERE
 213         Level.ERROR,   // FATAL
 214         Level.OFF,     // OFF
 215     };
 216 
 217     final static Map<java.util.logging.Level, Level> julToSpiMap;
 218     static {
 219         Map<java.util.logging.Level, Level> map = new HashMap<>();
 220         if (mappedLevels.length != julLevels.length) {
 221             throw new ExceptionInInitializerError("Array lengths differ"
 222                 + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
 223                 + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
 224         }
 225         for (int i=0; i<julLevels.length; i++) {
 226             map.put(julLevels[i], mappedLevels[i]);
 227         }
 228         julToSpiMap = Collections.unmodifiableMap(map);
 229     }
 230 
 231     public static class MyBundle extends ResourceBundle {
 232 
 233         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
 234 
 235         @Override
 236         protected Object handleGetObject(String key) {
 237             if (key.contains(" (translated)")) {
 238                 throw new RuntimeException("Unexpected key: " + key);
 239             }
 240             return map.computeIfAbsent(key, k -> k + " (translated)");
 241         }
 242 
 243         @Override
 244         public Enumeration<String> getKeys() {
 245             return Collections.enumeration(map.keySet());
 246         }
 247 
 248     }
 249 
 250     public static class MyHandler extends Handler {
 251 
 252         @Override
 253         public java.util.logging.Level getLevel() {
 254             return java.util.logging.Level.ALL;
 255         }
 256 
 257         @Override
 258         public void publish(LogRecord record) {
 259             eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
 260                     true, record.getLoggerName(),
 261                     record.getSourceClassName(),
 262                     record.getSourceMethodName(),
 263                     record.getLevel(),
 264                     record.getResourceBundle(), record.getMessage(),
 265                     record.getThrown(), record.getParameters()));
 266         }
 267         @Override
 268         public void flush() {
 269         }
 270         @Override
 271         public void close() throws SecurityException {
 272         }
 273 
 274     }
 275 
 276     public static class MyLoggerBundle extends MyBundle {
 277 
 278     }
 279 
 280 
 281     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
 282 
 283     static void setSecurityManager() {
 284         if (System.getSecurityManager() == null) {
 285             Policy.setPolicy(new SimplePolicy(allowAll, allowControl));
 286             System.setSecurityManager(new SecurityManager());
 287         }
 288     }
 289 
 290     public static void main(String[] args) {
 291         if (args.length == 0)
 292             args = new String[] {
 293                 "NOSECURITY",
 294                 "NOPERMISSIONS",
 295                 "WITHPERMISSIONS"
 296             };
 297 
 298         final java.util.logging.Logger appSink = LogManager.demandLoggerFor("foo", DefaultLoggerFinderTest.class);
 299         final java.util.logging.Logger sysSink = LogManager.demandLoggerFor("foo", Thread.class);
 300         appSink.addHandler(new MyHandler());
 301         sysSink.addHandler(new MyHandler());
 302         appSink.setUseParentHandlers(VERBOSE);
 303         sysSink.setUseParentHandlers(VERBOSE);
 304 
 305         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 306             LoggerFinder provider;
 307             switch (testCase) {
 308                 case NOSECURITY:
 309                     System.out.println("\n*** Without Security Manager\n");
 310                     provider = LoggerFinder.getLoggerFinder();
 311                     test(provider, true, appSink, sysSink);
 312                     System.out.println("Tetscase count: " + sequencer.get());
 313                     break;
 314                 case NOPERMISSIONS:
 315                     System.out.println("\n*** With Security Manager, without permissions\n");
 316                     setSecurityManager();
 317                     try {
 318                         provider = LoggerFinder.getLoggerFinder();
 319                         throw new RuntimeException("Expected exception not raised");
 320                     } catch (AccessControlException x) {
 321                         if (!LoggerFinder.LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
 322                             throw new RuntimeException("Unexpected permission check", x);
 323                         }
 324                         final boolean control = allowControl.get().get();
 325                         try {
 326                             allowControl.get().set(true);
 327                             provider = LoggerFinder.getLoggerFinder();
 328                         } finally {
 329                             allowControl.get().set(control);
 330                         }
 331                     }
 332                     test(provider, false, appSink, sysSink);
 333                     System.out.println("Tetscase count: " + sequencer.get());
 334                     break;
 335                 case WITHPERMISSIONS:
 336                     System.out.println("\n*** With Security Manager, with control permission\n");
 337                     setSecurityManager();
 338                     final boolean control = allowControl.get().get();
 339                     try {
 340                         allowControl.get().set(true);
 341                         provider = LoggerFinder.getLoggerFinder();
 342                         test(provider, true, appSink, sysSink);
 343                     } finally {
 344                         allowControl.get().set(control);
 345                     }
 346                     break;
 347                 default:
 348                     throw new RuntimeException("Unknown test case: " + testCase);
 349             }
 350         });
 351         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 352     }
 353 
 354     public static void test(LoggerFinder provider,
 355             boolean hasRequiredPermissions,
 356             java.util.logging.Logger appSink,
 357             java.util.logging.Logger sysSink) {
 358 
 359         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 360         final Map<Logger, String> loggerDescMap = new HashMap<>();
 361 
 362 
 363         Logger appLogger1 = null;
 364         try {
 365             appLogger1 = provider.getLogger("foo", DefaultLoggerFinderTest.class);
 366             loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")");
 367             if (!hasRequiredPermissions) {
 368                 throw new RuntimeException("Managed to obtain a logger without permission");
 369             }
 370         } catch (AccessControlException acx) {
 371             if (hasRequiredPermissions) {
 372                 throw new RuntimeException("Unexpected security exception: ", acx);
 373             }
 374             if (!acx.getPermission().equals(LoggerFinder.LOGGERFINDER_PERMISSION)) {
 375                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 376             }
 377             System.out.println("Got expected exception for logger: " + acx);
 378             boolean old = allowControl.get().get();
 379             allowControl.get().set(true);
 380             try {
 381                 appLogger1 =provider.getLogger("foo", DefaultLoggerFinderTest.class);
 382                 loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")");
 383             } finally {
 384                 allowControl.get().set(old);
 385             }
 386         }
 387 
 388         Logger sysLogger1 = null;
 389         try {
 390             sysLogger1 = provider.getLogger("foo", Thread.class);
 391             loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")");
 392             if (!hasRequiredPermissions) {
 393                 throw new RuntimeException("Managed to obtain a system logger without permission");
 394             }
 395         } catch (AccessControlException acx) {
 396             if (hasRequiredPermissions) {
 397                 throw new RuntimeException("Unexpected security exception: ", acx);
 398             }
 399             if (!acx.getPermission().equals(LoggerFinder.LOGGERFINDER_PERMISSION)) {
 400                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 401             }
 402             System.out.println("Got expected exception for system logger: " + acx);
 403             boolean old = allowControl.get().get();
 404             allowControl.get().set(true);
 405             try {
 406                 sysLogger1 = provider.getLogger("foo", Thread.class);
 407                 loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")");
 408             } finally {
 409                 allowControl.get().set(old);
 410             }
 411         }
 412         if (appLogger1 == sysLogger1) {
 413             throw new RuntimeException("identical loggers");
 414         }
 415 
 416         Logger appLogger2 = null;
 417         try {
 418             appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class);
 419             loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)");
 420             if (!hasRequiredPermissions) {
 421                 throw new RuntimeException("Managed to obtain a logger without permission");
 422             }
 423         } catch (AccessControlException acx) {
 424             if (hasRequiredPermissions) {
 425                 throw new RuntimeException("Unexpected security exception: ", acx);
 426             }
 427             if (!acx.getPermission().equals(LoggerFinder.LOGGERFINDER_PERMISSION)) {
 428                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 429             }
 430             System.out.println("Got expected exception for logger: " + acx);
 431             boolean old = allowControl.get().get();
 432             allowControl.get().set(true);
 433             try {
 434                 appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class);
 435                 loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)");
 436             } finally {
 437                 allowControl.get().set(old);
 438             }
 439         }
 440 
 441         Logger sysLogger2 = null;
 442         try {
 443             sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
 444             loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)");
 445             if (!hasRequiredPermissions) {
 446                 throw new RuntimeException("Managed to obtain a system logger without permission");
 447             }
 448         } catch (AccessControlException acx) {
 449             if (hasRequiredPermissions) {
 450                 throw new RuntimeException("Unexpected security exception: ", acx);
 451             }
 452             if (!acx.getPermission().equals(LoggerFinder.LOGGERFINDER_PERMISSION)) {
 453                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 454             }
 455             System.out.println("Got expected exception for localized system logger: " + acx);
 456             boolean old = allowControl.get().get();
 457             allowControl.get().set(true);
 458             try {
 459                 sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
 460                 loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)");
 461             } finally {
 462                 allowControl.get().set(old);
 463             }
 464         }
 465         if (appLogger2 == sysLogger2) {
 466             throw new RuntimeException("identical loggers");
 467         }
 468         if (appLogger2 == appLogger1) {
 469             throw new RuntimeException("identical loggers");
 470         }
 471         if (sysLogger2 == sysLogger1) {
 472             throw new RuntimeException("identical loggers");
 473         }
 474 
 475 
 476         testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
 477         testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
 478         testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
 479         testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
 480 
 481 
 482         Logger appLogger3 = System.getLogger("foo");
 483         loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")");
 484 
 485         testLogger(provider, loggerDescMap, "foo", null, appLogger3, appSink);
 486 
 487         Logger appLogger4 =
 488                 System.getLogger("foo", loggerBundle);
 489         loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)");
 490 
 491         if (appLogger4 == appLogger1) {
 492             throw new RuntimeException("identical loggers");
 493         }
 494 
 495         testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appSink);
 496 
 497         Logger sysLogger3 = accessSystemLogger.getLogger("foo");
 498         loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")");
 499 
 500         testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysSink);
 501 
 502         Logger sysLogger4 =
 503                 accessSystemLogger.getLogger("foo", loggerBundle);
 504         loggerDescMap.put(appLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
 505 
 506         if (sysLogger4 == sysLogger1) {
 507             throw new RuntimeException("identical loggers");
 508         }
 509 
 510         testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysSink);
 511 
 512     }
 513 
 514     public static class Foo {
 515 
 516     }
 517 
 518     static void verbose(String msg) {
 519        if (VERBOSE) {
 520            System.out.println(msg);
 521        }
 522     }
 523 
 524     static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
 525         boolean before = allowAll.get().get();
 526         try {
 527             allowAll.get().set(true);
 528             sink.setLevel(loggerLevel);
 529         } finally {
 530             allowAll.get().set(before);
 531         }
 532     }
 533 
 534 
 535     // Calls the 8 methods defined on Logger and verify the
 536     // parameters received by the underlying Logger Impl
 537     // logger.
 538     private static void testLogger(LoggerFinder provider,
 539             Map<Logger, String> loggerDescMap,
 540             String name,
 541             ResourceBundle loggerBundle,
 542             Logger logger,
 543             java.util.logging.Logger sink) {
 544 
 545         System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]");
 546         final java.util.logging.Level OFF = java.util.logging.Level.OFF;
 547 
 548         Foo foo = new Foo();
 549         String fooMsg = foo.toString();
 550         for (java.util.logging.Level loggerLevel : julLevels) {
 551             setLevel(sink, loggerLevel);
 552             for (Level messageLevel : Level.values()) {
 553                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 554                 String desc = "logger.log(messageLevel, foo): loggerLevel="
 555                         + loggerLevel+", messageLevel="+messageLevel;
 556                 LogEvent expected =
 557                         LogEvent.of(
 558                             sequencer.get(),
 559                             julLevel.intValue() >= loggerLevel.intValue(),
 560                             name, julLevel, (ResourceBundle)null,
 561                             fooMsg, (Throwable)null, (Object[])null);
 562                 logger.log(messageLevel, foo);
 563                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 564                     if (eventQueue.poll() != null) {
 565                         throw new RuntimeException("unexpected event in queue for " + desc);
 566                     }
 567                 } else {
 568                     LogEvent actual =  eventQueue.poll();
 569                     if (!expected.equals(actual)) {
 570                         throw new RuntimeException("mismatch for " + desc
 571                                 + "\n\texpected=" + expected
 572                                 + "\n\t  actual=" + actual);
 573                     } else {
 574                         verbose("Got expected results for "
 575                                 + desc + "\n\t" + expected);
 576                     }
 577                 }
 578             }
 579         }
 580 
 581         String msg = "blah";
 582         for (java.util.logging.Level loggerLevel : julLevels) {
 583             setLevel(sink, loggerLevel);
 584             for (Level messageLevel : Level.values()) {
 585                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 586                 String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
 587                         + loggerLevel+", messageLevel="+messageLevel;
 588                 LogEvent expected =
 589                         LogEvent.of(
 590                             sequencer.get(),
 591                             julLevel.intValue() >= loggerLevel.intValue(),
 592                             name, julLevel, loggerBundle,
 593                             msg, (Throwable)null, (Object[])null);
 594                 logger.log(messageLevel, msg);
 595                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 596                     if (eventQueue.poll() != null) {
 597                         throw new RuntimeException("unexpected event in queue for " + desc);
 598                     }
 599                 } else {
 600                     LogEvent actual =  eventQueue.poll();
 601                     if (!expected.equals(actual)) {
 602                         throw new RuntimeException("mismatch for " + desc
 603                                 + "\n\texpected=" + expected
 604                                 + "\n\t  actual=" + actual);
 605                     } else {
 606                         verbose("Got expected results for "
 607                                 + desc + "\n\t" + expected);
 608                     }
 609                 }
 610             }
 611         }
 612 
 613         Supplier<String> fooSupplier = new Supplier<String>() {
 614             @Override
 615             public String get() {
 616                 return this.toString();
 617             }
 618         };
 619 
 620         for (java.util.logging.Level loggerLevel : julLevels) {
 621             setLevel(sink, loggerLevel);
 622             for (Level messageLevel : Level.values()) {
 623                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 624                 String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
 625                         + loggerLevel+", messageLevel="+messageLevel;
 626                 LogEvent expected =
 627                         LogEvent.of(
 628                             sequencer.get(),
 629                             julLevel.intValue() >= loggerLevel.intValue(),
 630                             name, julLevel, (ResourceBundle)null,
 631                             fooSupplier.get(),
 632                             (Throwable)null, (Object[])null);
 633                 logger.log(messageLevel, fooSupplier);
 634                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 635                     if (eventQueue.poll() != null) {
 636                         throw new RuntimeException("unexpected event in queue for " + desc);
 637                     }
 638                 } else {
 639                     LogEvent actual =  eventQueue.poll();
 640                     if (!expected.equals(actual)) {
 641                         throw new RuntimeException("mismatch for " + desc
 642                                 + "\n\texpected=" + expected
 643                                 + "\n\t  actual=" + actual);
 644                     } else {
 645                         verbose("Got expected results for "
 646                                 + desc + "\n\t" + expected);
 647                     }
 648                 }
 649             }
 650         }
 651 
 652         String format = "two params [{1} {2}]";
 653         Object arg1 = foo;
 654         Object arg2 = msg;
 655         for (java.util.logging.Level loggerLevel : julLevels) {
 656             setLevel(sink, loggerLevel);
 657             for (Level messageLevel : Level.values()) {
 658                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 659                 String desc = "logger.log(messageLevel, format, params...): loggerLevel="
 660                         + loggerLevel+", messageLevel="+messageLevel;
 661                 LogEvent expected =
 662                         LogEvent.of(
 663                             sequencer.get(),
 664                             julLevel.intValue() >= loggerLevel.intValue(),
 665                             name, julLevel, loggerBundle,
 666                             format, (Throwable)null, new Object[] {arg1, arg2});
 667                 logger.log(messageLevel, format, arg1, arg2);
 668                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 669                     if (eventQueue.poll() != null) {
 670                         throw new RuntimeException("unexpected event in queue for " + desc);
 671                     }
 672                 } else {
 673                     LogEvent actual =  eventQueue.poll();
 674                     if (!expected.equals(actual)) {
 675                         throw new RuntimeException("mismatch for " + desc
 676                                 + "\n\texpected=" + expected
 677                                 + "\n\t  actual=" + actual);
 678                     } else {
 679                         verbose("Got expected results for "
 680                                 + desc + "\n\t" + expected);
 681                     }
 682                 }
 683             }
 684         }
 685 
 686         Throwable thrown = new Exception("OK: log me!");
 687         for (java.util.logging.Level loggerLevel : julLevels) {
 688             setLevel(sink, loggerLevel);
 689             for (Level messageLevel : Level.values()) {
 690                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 691                 String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
 692                         + loggerLevel+", messageLevel="+messageLevel;
 693                 LogEvent expected =
 694                         LogEvent.of(
 695                             sequencer.get(),
 696                             julLevel.intValue() >= loggerLevel.intValue(),
 697                             name, julLevel, loggerBundle,
 698                             msg, thrown, (Object[]) null);
 699                 logger.log(messageLevel, msg, thrown);
 700                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 701                     if (eventQueue.poll() != null) {
 702                         throw new RuntimeException("unexpected event in queue for " + desc);
 703                     }
 704                 } else {
 705                     LogEvent actual =  eventQueue.poll();
 706                     if (!expected.equals(actual)) {
 707                         throw new RuntimeException("mismatch for " + desc
 708                                 + "\n\texpected=" + expected
 709                                 + "\n\t  actual=" + actual);
 710                     } else {
 711                         verbose("Got expected results for "
 712                                 + desc + "\n\t" + expected);
 713                     }
 714                 }
 715             }
 716         }
 717 
 718 
 719         for (java.util.logging.Level loggerLevel : julLevels) {
 720             setLevel(sink, loggerLevel);
 721             for (Level messageLevel : Level.values()) {
 722                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 723                 String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
 724                         + loggerLevel+", messageLevel="+messageLevel;
 725                 LogEvent expected =
 726                         LogEvent.of(
 727                             sequencer.get(),
 728                             julLevel.intValue() >= loggerLevel.intValue(),
 729                             name, julLevel, (ResourceBundle)null,
 730                             fooSupplier.get(),
 731                             (Throwable)thrown, (Object[])null);
 732                 logger.log(messageLevel, fooSupplier, thrown);
 733                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 734                     if (eventQueue.poll() != null) {
 735                         throw new RuntimeException("unexpected event in queue for " + desc);
 736                     }
 737                 } else {
 738                     LogEvent actual =  eventQueue.poll();
 739                     if (!expected.equals(actual)) {
 740                         throw new RuntimeException("mismatch for " + desc
 741                                 + "\n\texpected=" + expected
 742                                 + "\n\t  actual=" + actual);
 743                     } else {
 744                         verbose("Got expected results for "
 745                                 + desc + "\n\t" + expected);
 746                     }
 747                 }
 748             }
 749         }
 750 
 751         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 752         for (java.util.logging.Level loggerLevel : julLevels) {
 753             setLevel(sink, loggerLevel);
 754             for (Level messageLevel : Level.values()) {
 755                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 756                 String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
 757                         + loggerLevel+", messageLevel="+messageLevel;
 758                 LogEvent expected =
 759                         LogEvent.of(
 760                             sequencer.get(),
 761                             julLevel.intValue() >= loggerLevel.intValue(),
 762                             name, julLevel, bundle,
 763                             format, (Throwable)null, new Object[] {foo, msg});
 764                 logger.log(messageLevel, bundle, format, foo, msg);
 765                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 766                     if (eventQueue.poll() != null) {
 767                         throw new RuntimeException("unexpected event in queue for " + desc);
 768                     }
 769                 } else {
 770                     LogEvent actual =  eventQueue.poll();
 771                     if (!expected.equals(actual)) {
 772                         throw new RuntimeException("mismatch for " + desc
 773                                 + "\n\texpected=" + expected
 774                                 + "\n\t  actual=" + actual);
 775                     } else {
 776                         verbose("Got expected results for "
 777                                 + desc + "\n\t" + expected);
 778                     }
 779                 }
 780             }
 781         }
 782 
 783         for (java.util.logging.Level loggerLevel : julLevels) {
 784             setLevel(sink, loggerLevel);
 785             for (Level messageLevel : Level.values()) {
 786                 java.util.logging.Level julLevel = mapToJul(messageLevel);
 787                 String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
 788                         + loggerLevel+", messageLevel="+messageLevel;
 789                 LogEvent expected =
 790                         LogEvent.of(
 791                             sequencer.get(),
 792                             julLevel.intValue() >= loggerLevel.intValue(),
 793                             name, julLevel, bundle,
 794                             msg, thrown, (Object[]) null);
 795                 logger.log(messageLevel, bundle, msg, thrown);
 796                 if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
 797                     if (eventQueue.poll() != null) {
 798                         throw new RuntimeException("unexpected event in queue for " + desc);
 799                     }
 800                 } else {
 801                     LogEvent actual =  eventQueue.poll();
 802                     if (!expected.equals(actual)) {
 803                         throw new RuntimeException("mismatch for " + desc
 804                                 + "\n\texpected=" + expected
 805                                 + "\n\t  actual=" + actual);
 806                     } else {
 807                         verbose("Got expected results for "
 808                                 + desc + "\n\t" + expected);
 809                     }
 810                 }
 811             }
 812         }
 813     }
 814 
 815     final static class PermissionsBuilder {
 816         final Permissions perms;
 817         public PermissionsBuilder() {
 818             this(new Permissions());
 819         }
 820         public PermissionsBuilder(Permissions perms) {
 821             this.perms = perms;
 822         }
 823         public PermissionsBuilder add(Permission p) {
 824             perms.add(p);
 825             return this;
 826         }
 827         public PermissionsBuilder addAll(PermissionCollection col) {
 828             if (col != null) {
 829                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 830                     perms.add(e.nextElement());
 831                 }
 832             }
 833             return this;
 834         }
 835         public Permissions toPermissions() {
 836             final PermissionsBuilder builder = new PermissionsBuilder();
 837             builder.addAll(perms);
 838             return builder.perms;
 839         }
 840     }
 841 
 842     public static class SimplePolicy extends Policy {
 843 
 844         final Permissions permissions;
 845         final Permissions withControlPermissions;
 846         final Permissions allPermissions;
 847         final ThreadLocal<AtomicBoolean> allowAll;
 848         final ThreadLocal<AtomicBoolean> allowControl;
 849         public SimplePolicy(ThreadLocal<AtomicBoolean> allowAll,
 850                 ThreadLocal<AtomicBoolean> allowControl) {
 851             this.allowAll = allowAll;
 852             this.allowControl = allowControl;
 853             permissions = new Permissions();
 854 
 855             withControlPermissions = new Permissions();
 856             withControlPermissions.add(LoggerFinder.LOGGERFINDER_PERMISSION);
 857 
 858             // these are used for configuring the test itself...
 859             allPermissions = new Permissions();
 860             allPermissions.add(new java.security.AllPermission());
 861         }
 862 
 863         @Override
 864         public boolean implies(ProtectionDomain domain, Permission permission) {
 865             if (allowAll.get().get()) return allPermissions.implies(permission);
 866             if (allowControl.get().get()) return withControlPermissions.implies(permission);
 867             return permissions.implies(permission);
 868         }
 869 
 870         @Override
 871         public PermissionCollection getPermissions(CodeSource codesource) {
 872             return new PermissionsBuilder().addAll(
 873                     allowAll.get().get() ? allPermissions :
 874                     allowControl.get().get()
 875                     ? withControlPermissions : permissions).toPermissions();
 876         }
 877 
 878         @Override
 879         public PermissionCollection getPermissions(ProtectionDomain domain) {
 880             return new PermissionsBuilder().addAll(
 881                     allowAll.get().get() ? allPermissions :
 882                     allowControl.get().get()
 883                     ? withControlPermissions : permissions).toPermissions();
 884         }
 885     }
 886 }