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