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.lang.System.LoggerFinder;
  44 import java.lang.System.Logger;
  45 import java.lang.System.Logger.Level;
  46 import java.util.logging.Handler;
  47 import java.util.logging.LogRecord;
  48 import java.util.stream.Stream;
  49 
  50 /**
  51  * @test
  52  * @bug     8140364
  53  * @summary Tests default loggers returned by System.getLogger, and in
  54  *          particular the implementation of the the System.Logger method
  55  *          performed by the default binding.
  56  *
  57  * @build DefaultLoggerTest AccessSystemLogger
  58  * @run driver AccessSystemLogger
  59  * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOSECURITY
  60  * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOPERMISSIONS
  61  * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest WITHPERMISSIONS
  62  * @author danielfuchs
  63  */
  64 public class DefaultLoggerTest {
  65 
  66     final static AtomicLong sequencer = new AtomicLong();
  67     final static boolean VERBOSE = false;
  68     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
  69         @Override
  70         protected AtomicBoolean initialValue() {
  71             return  new AtomicBoolean(false);
  72         }
  73     };
  74     static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
  75         @Override
  76         protected AtomicBoolean initialValue() {
  77             return  new AtomicBoolean(false);
  78         }
  79     };
  80 
  81     public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
  82 
  83     public static final class LogEvent {
  84 
  85         public LogEvent() {
  86             this(sequencer.getAndIncrement());
  87         }
  88 
  89         LogEvent(long sequenceNumber) {
  90             this.sequenceNumber = sequenceNumber;
  91         }
  92 
  93         long sequenceNumber;
  94         boolean isLoggable;
  95         String loggerName;
  96         java.util.logging.Level level;
  97         ResourceBundle bundle;
  98         Throwable thrown;
  99         Object[] args;
 100         String msg;
 101         String className;
 102         String methodName;
 103 
 104         Object[] toArray() {
 105             return new Object[] {
 106                 sequenceNumber,
 107                 isLoggable,
 108                 loggerName,
 109                 level,
 110                 bundle,
 111                 thrown,
 112                 args,
 113                 msg,
 114                 className,
 115                 methodName,
 116             };
 117         }
 118 
 119         @Override
 120         public String toString() {
 121             return Arrays.deepToString(toArray());
 122         }
 123 
 124         @Override
 125         public boolean equals(Object obj) {
 126             return obj instanceof LogEvent
 127                     && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
 128         }
 129 
 130         @Override
 131         public int hashCode() {
 132             return Objects.hash(toArray());
 133         }
 134         public static LogEvent of(long sequenceNumber,
 135                 boolean isLoggable, String name,
 136                 java.util.logging.Level level, ResourceBundle bundle,
 137                 String key, Throwable thrown, Object... params) {
 138             return LogEvent.of(sequenceNumber, isLoggable, name,
 139                     DefaultLoggerTest.class.getName(),
 140                     "testLogger", level, bundle, key,
 141                     thrown, params);
 142         }
 143         public static LogEvent of(long sequenceNumber,
 144                 boolean isLoggable, String name,
 145                 String className, String methodName,
 146                 java.util.logging.Level level, ResourceBundle bundle,
 147                 String key, Throwable thrown, Object... params) {
 148             LogEvent evt = new LogEvent(sequenceNumber);
 149             evt.loggerName = name;
 150             evt.level = level;
 151             evt.args = params;
 152             evt.bundle = bundle;
 153             evt.thrown = thrown;
 154             evt.msg = key;
 155             evt.isLoggable = isLoggable;
 156             evt.className = className;
 157             evt.methodName = methodName;
 158             return evt;
 159         }
 160 
 161     }
 162 
 163     static java.util.logging.Level mapToJul(Level level) {
 164         switch (level) {
 165             case ALL: return java.util.logging.Level.ALL;
 166             case TRACE: return java.util.logging.Level.FINER;
 167             case DEBUG: return java.util.logging.Level.FINE;
 168             case INFO: return java.util.logging.Level.INFO;
 169             case WARNING: return java.util.logging.Level.WARNING;
 170             case ERROR: return java.util.logging.Level.SEVERE;
 171             case OFF: return java.util.logging.Level.OFF;
 172         }
 173         throw new InternalError("No such level: " + level);
 174     }
 175 
 176     static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
 177         boolean before = allowAll.get().get();
 178         try {
 179             allowAll.get().set(true);
 180             sink.setLevel(loggerLevel);
 181         } finally {
 182             allowAll.get().set(before);
 183         }
 184     }
 185 
 186     public static class MyHandler extends Handler {
 187 
 188         @Override
 189         public java.util.logging.Level getLevel() {
 190             return java.util.logging.Level.ALL;
 191         }
 192 
 193         @Override
 194         public void publish(LogRecord record) {
 195             eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
 196                     true, record.getLoggerName(),
 197                     record.getSourceClassName(),
 198                     record.getSourceMethodName(),
 199                     record.getLevel(),
 200                     record.getResourceBundle(), record.getMessage(),
 201                     record.getThrown(), record.getParameters()));
 202         }
 203         @Override
 204         public void flush() {
 205         }
 206         @Override
 207         public void close() throws SecurityException {
 208         }
 209 
 210     }
 211     public static class MyBundle extends ResourceBundle {
 212 
 213         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
 214 
 215         @Override
 216         protected Object handleGetObject(String key) {
 217             if (key.contains(" (translated)")) {
 218                 throw new RuntimeException("Unexpected key: " + key);
 219             }
 220             return map.computeIfAbsent(key, k -> k + " (translated)");
 221         }
 222 
 223         @Override
 224         public Enumeration<String> getKeys() {
 225             return Collections.enumeration(map.keySet());
 226         }
 227 
 228     }
 229     public static class MyLoggerBundle extends MyBundle {
 230 
 231     }
 232 
 233     static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
 234 
 235     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
 236 
 237     static void setSecurityManager() {
 238         if (System.getSecurityManager() == null) {
 239             Policy.setPolicy(new SimplePolicy(allowControl, allowAll));
 240             System.setSecurityManager(new SecurityManager());
 241         }
 242     }
 243     public static void main(String[] args) {
 244         if (args.length == 0)
 245             args = new String[] {
 246                 "NOSECURITY",
 247                 "NOPERMISSIONS",
 248                 "WITHPERMISSIONS"
 249             };
 250 
 251         // 1. Obtain destination loggers directly from the LoggerFinder
 252         //   - LoggerFinder.getLogger("foo", type)
 253 
 254 
 255         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 256             switch (testCase) {
 257                 case NOSECURITY:
 258                     System.out.println("\n*** Without Security Manager\n");
 259                     test(true);
 260                     System.out.println("Tetscase count: " + sequencer.get());
 261                     break;
 262                 case NOPERMISSIONS:
 263                     System.out.println("\n*** With Security Manager, without permissions\n");
 264                     setSecurityManager();
 265                     test(false);
 266                     System.out.println("Tetscase count: " + sequencer.get());
 267                     break;
 268                 case WITHPERMISSIONS:
 269                     System.out.println("\n*** With Security Manager, with control permission\n");
 270                     setSecurityManager();
 271                     final boolean control = allowControl.get().get();
 272                     try {
 273                         allowControl.get().set(true);
 274                         test(true);
 275                     } finally {
 276                         allowControl.get().set(control);
 277                     }
 278                     break;
 279                 default:
 280                     throw new RuntimeException("Unknown test case: " + testCase);
 281             }
 282         });
 283         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 284     }
 285 
 286     public static void test(boolean hasRequiredPermissions) {
 287 
 288         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 289         final Map<Logger, String> loggerDescMap = new HashMap<>();
 290 
 291 
 292         // 1. Test loggers returned by:
 293         //   - System.getLogger("foo")
 294         //   - and AccessSystemLogger.getLogger("foo")
 295         Logger sysLogger1 = null;
 296         try {
 297             sysLogger1 = accessSystemLogger.getLogger("foo");
 298             loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
 299         } catch (AccessControlException acx) {
 300             if (hasRequiredPermissions) {
 301                 throw new RuntimeException("Unexpected security exception: ", acx);
 302             }
 303             if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
 304                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 305             }
 306             throw new RuntimeException("unexpected exception: " + acx, acx);
 307         }
 308 
 309         Logger appLogger1 = System.getLogger("foo");
 310         loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
 311 
 312         if (appLogger1 == sysLogger1) {
 313             throw new RuntimeException("identical loggers");
 314         }
 315 
 316         // 2. Test loggers returned by:
 317         //   - System.getLogger(\"foo\", loggerBundle)
 318         //   - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
 319         Logger appLogger2 =
 320                 System.getLogger("foo", loggerBundle);
 321         loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
 322 
 323         Logger sysLogger2 = null;
 324         try {
 325             sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);
 326             loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
 327         } catch (AccessControlException acx) {
 328             if (hasRequiredPermissions) {
 329                 throw new RuntimeException("Unexpected security exception: ", acx);
 330             }
 331             if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
 332                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 333             }
 334             throw new RuntimeException("unexpected exception: " + acx, acx);
 335         }
 336         if (appLogger2 == sysLogger2) {
 337             throw new RuntimeException("identical loggers");
 338         }
 339 
 340         final java.util.logging.Logger appSink;
 341         final java.util.logging.Logger sysSink;
 342         final java.util.logging.Handler appHandler;
 343         final java.util.logging.Handler sysHandler;
 344         final  LoggerFinder provider;
 345         allowAll.get().set(true);
 346         try {
 347             appSink = java.util.logging.Logger.getLogger("foo");
 348             sysSink = accessSystemLogger.demandSystemLogger("foo");
 349             appSink.addHandler(appHandler = new MyHandler());
 350             sysSink.addHandler(sysHandler = new MyHandler());
 351             appSink.setUseParentHandlers(false);
 352             sysSink.setUseParentHandlers(false);
 353             provider = LoggerFinder.getLoggerFinder();
 354         } finally {
 355             allowAll.get().set(false);
 356         }
 357         try {
 358             testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
 359             testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
 360             testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
 361             testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
 362         } finally {
 363             allowAll.get().set(true);
 364             try {
 365                 appSink.removeHandler(appHandler);
 366                 sysSink.removeHandler(sysHandler);
 367                 sysSink.setLevel(null);
 368                 appSink.setLevel(null);
 369             } finally {
 370                 allowAll.get().set(false);
 371             }
 372         }
 373     }
 374 
 375     public static class Foo {
 376 
 377     }
 378 
 379     static void verbose(String msg) {
 380        if (VERBOSE) {
 381            System.out.println(msg);
 382        }
 383     }
 384 
 385     // Calls the 8 methods defined on Logger and verify the
 386     // parameters received by the underlying BaseLoggerFinder.LoggerImpl
 387     // logger.
 388     private static void testLogger(LoggerFinder provider,
 389             Map<Logger, String> loggerDescMap,
 390             String name,
 391             ResourceBundle loggerBundle,
 392             Logger logger,
 393             java.util.logging.Logger sink) {
 394 
 395         System.out.println("Testing " + loggerDescMap.get(logger));
 396 
 397         Foo foo = new Foo();
 398         String fooMsg = foo.toString();
 399         for (Level loggerLevel : Level.values()) {
 400             setLevel(sink, mapToJul(loggerLevel));
 401             for (Level messageLevel : Level.values()) {
 402                 String desc = "logger.log(messageLevel, foo): loggerLevel="
 403                         + loggerLevel+", messageLevel="+messageLevel;
 404 
 405                 LogEvent expected =
 406                         LogEvent.of(
 407                             sequencer.get(),
 408                             messageLevel.compareTo(loggerLevel) >= 0,
 409                             name, mapToJul(messageLevel), (ResourceBundle)null,
 410                             fooMsg, (Throwable)null, (Object[])null);
 411                 logger.log(messageLevel, foo);
 412                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 413                     if (eventQueue.poll() != null) {
 414                         throw new RuntimeException("unexpected event in queue for " + desc);
 415                     }
 416                 } else {
 417                     LogEvent actual = eventQueue.poll();
 418                     if (!expected.equals(actual)) {
 419                         throw new RuntimeException("mismatch for " + desc
 420                                 + "\n\texpected=" + expected
 421                                 + "\n\t  actual=" + actual);
 422                     } else {
 423                         verbose("Got expected results for "
 424                                 + desc + "\n\t" + expected);
 425                     }
 426                 }
 427             }
 428         }
 429 
 430         String msg = "blah";
 431         for (Level loggerLevel : Level.values()) {
 432             setLevel(sink, mapToJul(loggerLevel));
 433             for (Level messageLevel : Level.values()) {
 434                 String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
 435                         + loggerLevel+", messageLevel="+messageLevel;
 436                 LogEvent expected =
 437                         LogEvent.of(
 438                             sequencer.get(),
 439                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 440                             name, mapToJul(messageLevel), loggerBundle,
 441                             msg, (Throwable)null, (Object[])null);
 442                 logger.log(messageLevel, msg);
 443                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 444                     if (eventQueue.poll() != null) {
 445                         throw new RuntimeException("unexpected event in queue for " + desc);
 446                     }
 447                 } else {
 448                     LogEvent actual =  eventQueue.poll();
 449                     if (!expected.equals(actual)) {
 450                         throw new RuntimeException("mismatch for " + desc
 451                             + "\n\texpected=" + expected
 452                             + "\n\t  actual=" + actual);
 453                     } else {
 454                         verbose("Got expected results for "
 455                             + desc + "\n\t" + expected);
 456                     }
 457                 }
 458             }
 459         }
 460 
 461         Supplier<String> fooSupplier = new Supplier<String>() {
 462             @Override
 463             public String get() {
 464                 return this.toString();
 465             }
 466         };
 467 
 468         for (Level loggerLevel : Level.values()) {
 469             setLevel(sink, mapToJul(loggerLevel));
 470             for (Level messageLevel : Level.values()) {
 471                 String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
 472                         + loggerLevel+", messageLevel="+messageLevel;
 473                 LogEvent expected =
 474                         LogEvent.of(
 475                             sequencer.get(),
 476                             messageLevel.compareTo(loggerLevel) >= 0,
 477                             name, mapToJul(messageLevel), (ResourceBundle)null,
 478                             fooSupplier.get(),
 479                             (Throwable)null, (Object[])null);
 480                 logger.log(messageLevel, fooSupplier);
 481                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 482                     if (eventQueue.poll() != null) {
 483                         throw new RuntimeException("unexpected event in queue for " + desc);
 484                     }
 485                 } else {
 486                     LogEvent actual =  eventQueue.poll();
 487                     if (!expected.equals(actual)) {
 488                         throw new RuntimeException("mismatch for " + desc
 489                                 + "\n\texpected=" + expected
 490                                 + "\n\t  actual=" + actual);
 491                     } else {
 492                         verbose("Got expected results for "
 493                                 + desc + "\n\t" + expected);
 494                     }
 495                 }
 496             }
 497         }
 498 
 499         String format = "two params [{1} {2}]";
 500         Object arg1 = foo;
 501         Object arg2 = msg;
 502         for (Level loggerLevel : Level.values()) {
 503             setLevel(sink, mapToJul(loggerLevel));
 504             for (Level messageLevel : Level.values()) {
 505                 String desc = "logger.log(messageLevel, format, params...): loggerLevel="
 506                         + loggerLevel+", messageLevel="+messageLevel;
 507                 LogEvent expected =
 508                         LogEvent.of(
 509                             sequencer.get(),
 510                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 511                             name, mapToJul(messageLevel), loggerBundle,
 512                             format, (Throwable)null, new Object[] {arg1, arg2});
 513                 logger.log(messageLevel, format, arg1, arg2);
 514                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 515                     if (eventQueue.poll() != null) {
 516                         throw new RuntimeException("unexpected event in queue for " + desc);
 517                     }
 518                 } else {
 519                     LogEvent actual =  eventQueue.poll();
 520                     if (!expected.equals(actual)) {
 521                         throw new RuntimeException("mismatch for " + desc
 522                                 + "\n\texpected=" + expected
 523                                 + "\n\t  actual=" + actual);
 524                     } else {
 525                         verbose("Got expected results for "
 526                             + desc + "\n\t" + expected);
 527                     }
 528                 }
 529             }
 530         }
 531 
 532         Throwable thrown = new Exception("OK: log me!");
 533         for (Level loggerLevel : Level.values()) {
 534             setLevel(sink, mapToJul(loggerLevel));
 535             for (Level messageLevel : Level.values()) {
 536                 String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
 537                         + loggerLevel+", messageLevel="+messageLevel;
 538                 LogEvent expected =
 539                         LogEvent.of(
 540                             sequencer.get(),
 541                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 542                             name, mapToJul(messageLevel), loggerBundle,
 543                             msg, thrown, (Object[]) null);
 544                 logger.log(messageLevel, msg, thrown);
 545                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 546                     if (eventQueue.poll() != null) {
 547                         throw new RuntimeException("unexpected event in queue for " + desc);
 548                     }
 549                 } else {
 550                     LogEvent actual =  eventQueue.poll();
 551                     if (!expected.equals(actual)) {
 552                         throw new RuntimeException("mismatch for " + desc
 553                             + "\n\texpected=" + expected
 554                             + "\n\t  actual=" + actual);
 555                     } else {
 556                         verbose("Got expected results for "
 557                                 + desc + "\n\t" + expected);
 558                     }
 559                 }
 560             }
 561         }
 562 
 563 
 564         for (Level loggerLevel : Level.values()) {
 565             setLevel(sink, mapToJul(loggerLevel));
 566             for (Level messageLevel : Level.values()) {
 567                 String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
 568                         + loggerLevel+", messageLevel="+messageLevel;
 569                 LogEvent expected =
 570                         LogEvent.of(
 571                             sequencer.get(),
 572                             messageLevel.compareTo(loggerLevel) >= 0,
 573                             name, mapToJul(messageLevel), (ResourceBundle)null,
 574                             fooSupplier.get(),
 575                             (Throwable)thrown, (Object[])null);
 576                 logger.log(messageLevel, fooSupplier, thrown);
 577                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 578                     if (eventQueue.poll() != null) {
 579                         throw new RuntimeException("unexpected event in queue for " + desc);
 580                     }
 581                 } else {
 582                     LogEvent actual =  eventQueue.poll();
 583                     if (!expected.equals(actual)) {
 584                         throw new RuntimeException("mismatch for " + desc
 585                                 + "\n\texpected=" + expected
 586                                 + "\n\t  actual=" + actual);
 587                     } else {
 588                         verbose("Got expected results for "
 589                                 + desc + "\n\t" + expected);
 590                     }
 591                 }
 592             }
 593         }
 594 
 595         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 596         for (Level loggerLevel : Level.values()) {
 597             setLevel(sink, mapToJul(loggerLevel));
 598             for (Level messageLevel : Level.values()) {
 599                 String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
 600                         + loggerLevel+", messageLevel="+messageLevel;
 601                 LogEvent expected =
 602                         LogEvent.of(
 603                             sequencer.get(),
 604                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 605                             name, mapToJul(messageLevel), bundle,
 606                             format, (Throwable)null, new Object[] {foo, msg});
 607                 logger.log(messageLevel, bundle, format, foo, msg);
 608                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 609                     if (eventQueue.poll() != null) {
 610                         throw new RuntimeException("unexpected event in queue for " + desc);
 611                     }
 612                 } else {
 613                     LogEvent actual =  eventQueue.poll();
 614                     if (!expected.equals(actual)) {
 615                         throw new RuntimeException("mismatch for " + desc
 616                             + "\n\texpected=" + expected
 617                             + "\n\t  actual=" + actual);
 618                     } else {
 619                         verbose("Got expected results for "
 620                             + desc + "\n\t" + expected);
 621                     }
 622                 }
 623             }
 624         }
 625 
 626         for (Level loggerLevel : Level.values()) {
 627             setLevel(sink, mapToJul(loggerLevel));
 628             for (Level messageLevel : Level.values()) {
 629                 String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
 630                         + loggerLevel+", messageLevel="+messageLevel;
 631                 LogEvent expected =
 632                         LogEvent.of(
 633                             sequencer.get(),
 634                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 635                             name, mapToJul(messageLevel), bundle,
 636                             msg, thrown, (Object[]) null);
 637                 logger.log(messageLevel, bundle, msg, thrown);
 638                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 639                     if (eventQueue.poll() != null) {
 640                         throw new RuntimeException("unexpected event in queue for " + desc);
 641                     }
 642                 } else {
 643                     LogEvent actual =  eventQueue.poll();
 644                     if (!expected.equals(actual)) {
 645                         throw new RuntimeException("mismatch for " + desc
 646                             + "\n\texpected=" + expected
 647                             + "\n\t  actual=" + actual);
 648                     } else {
 649                         verbose("Got expected results for "
 650                             + desc + "\n\t" + expected);
 651                     }
 652                 }
 653             }
 654         }
 655     }
 656 
 657     final static class PermissionsBuilder {
 658         final Permissions perms;
 659         public PermissionsBuilder() {
 660             this(new Permissions());
 661         }
 662         public PermissionsBuilder(Permissions perms) {
 663             this.perms = perms;
 664         }
 665         public PermissionsBuilder add(Permission p) {
 666             perms.add(p);
 667             return this;
 668         }
 669         public PermissionsBuilder addAll(PermissionCollection col) {
 670             if (col != null) {
 671                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 672                     perms.add(e.nextElement());
 673                 }
 674             }
 675             return this;
 676         }
 677         public Permissions toPermissions() {
 678             final PermissionsBuilder builder = new PermissionsBuilder();
 679             builder.addAll(perms);
 680             return builder.perms;
 681         }
 682     }
 683 
 684     public static class SimplePolicy extends Policy {
 685         static final RuntimePermission LOGGERFINDER_PERMISSION =
 686                 new RuntimePermission("loggerFinder");
 687         final Permissions permissions;
 688         final Permissions allPermissions;
 689         final Permissions controlPermissions;
 690         final ThreadLocal<AtomicBoolean> allowControl;
 691         final ThreadLocal<AtomicBoolean> allowAll;
 692         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAll) {
 693             this.allowControl = allowControl;
 694             this.allowAll = allowAll;
 695             permissions = new Permissions();
 696 
 697             // these are used for configuring the test itself...
 698             controlPermissions = new Permissions();
 699             controlPermissions.add(LOGGERFINDER_PERMISSION);
 700             allPermissions = new Permissions();
 701             allPermissions.add(new java.security.AllPermission());
 702 
 703         }
 704 
 705         @Override
 706         public boolean implies(ProtectionDomain domain, Permission permission) {
 707             if (allowAll.get().get()) return allPermissions.implies(permission);
 708             if (allowControl.get().get()) return controlPermissions.implies(permission);
 709             return permissions.implies(permission);
 710         }
 711 
 712         @Override
 713         public PermissionCollection getPermissions(CodeSource codesource) {
 714             return new PermissionsBuilder().addAll(allowAll.get().get()
 715                     ? allPermissions : allowControl.get().get()
 716                     ? controlPermissions : permissions).toPermissions();
 717         }
 718 
 719         @Override
 720         public PermissionCollection getPermissions(ProtectionDomain domain) {
 721             return new PermissionsBuilder().addAll(allowAll.get().get()
 722                     ? allPermissions : allowControl.get().get()
 723                     ? controlPermissions : permissions).toPermissions();
 724         }
 725     }
 726 }