1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.security.AccessControlException;
  24 import java.security.AccessController;
  25 import java.security.CodeSource;
  26 import java.security.Permission;
  27 import java.security.PermissionCollection;
  28 import java.security.Permissions;
  29 import java.security.Policy;
  30 import java.security.PrivilegedAction;
  31 import java.security.ProtectionDomain;
  32 import java.util.Arrays;
  33 import java.util.Collections;
  34 import java.util.Enumeration;
  35 import java.util.HashMap;
  36 import java.util.Map;
  37 import java.util.Objects;
  38 import java.util.Queue;
  39 import java.util.ResourceBundle;
  40 import java.util.concurrent.ArrayBlockingQueue;
  41 import java.util.concurrent.ConcurrentHashMap;
  42 import java.util.concurrent.atomic.AtomicBoolean;
  43 import java.util.concurrent.atomic.AtomicLong;
  44 import java.util.function.Supplier;
  45 import java.lang.System.LoggerFinder;
  46 import java.lang.System.Logger;
  47 import java.lang.System.Logger.Level;
  48 import java.util.stream.Stream;
  49 import java.lang.reflect.Module;
  50 import java.security.AllPermission;
  51 
  52 /**
  53  * @test
  54  * @bug     8140364
  55  * @summary Tests loggers returned by System.getLogger with a naive implementation
  56  *          of LoggerFinder, and in particular the default body of
  57  *          System.Logger methods.
  58  * @build CustomLoggerTest AccessSystemLogger
  59  * @run driver AccessSystemLogger
  60  * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOSECURITY
  61  * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOPERMISSIONS
  62  * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest WITHPERMISSIONS
  63  * @author danielfuchs
  64  */
  65 public class CustomLoggerTest {
  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 class MyBundle extends ResourceBundle {
  83 
  84         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
  85 
  86         @Override
  87         protected Object handleGetObject(String key) {
  88             if (key.contains(" (translated)")) {
  89                 throw new RuntimeException("Unexpected key: " + key);
  90             }
  91             return map.computeIfAbsent(key, k -> k + " (translated)");
  92         }
  93 
  94         @Override
  95         public Enumeration<String> getKeys() {
  96             return Collections.enumeration(map.keySet());
  97         }
  98 
  99     }
 100     public static class MyLoggerBundle extends MyBundle {
 101 
 102     }
 103 
 104 
 105     public static class BaseLoggerFinder extends LoggerFinder {
 106         final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
 107         final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
 108         public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
 109 
 110         // changing this to true requires changing the logic in the
 111         // test in order to load this class with a protection domain
 112         // that has the CONTROL_PERMISSION (e.g. by using a custom
 113         // system class loader.
 114         final boolean doChecks = false;
 115 
 116         public static final class LogEvent {
 117 
 118             public LogEvent() {
 119                 this(sequencer.getAndIncrement());
 120             }
 121 
 122             LogEvent(long sequenceNumber) {
 123                 this.sequenceNumber = sequenceNumber;
 124             }
 125 
 126             long sequenceNumber;
 127             boolean isLoggable;
 128             String loggerName;
 129             Level level;
 130             ResourceBundle bundle;
 131             Throwable thrown;
 132             Object[] args;
 133             Supplier<String> supplier;
 134             String msg;
 135 
 136             Object[] toArray() {
 137                 return new Object[] {
 138                     sequenceNumber,
 139                     isLoggable,
 140                     loggerName,
 141                     level,
 142                     bundle,
 143                     thrown,
 144                     args,
 145                     supplier,
 146                     msg,
 147                 };
 148             }
 149 
 150             @Override
 151             public String toString() {
 152                 return Arrays.deepToString(toArray());
 153             }
 154 
 155 
 156 
 157             @Override
 158             public boolean equals(Object obj) {
 159                 return obj instanceof LogEvent
 160                         && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
 161             }
 162 
 163             @Override
 164             public int hashCode() {
 165                 return Objects.hash(toArray());
 166             }
 167 
 168 
 169             public static LogEvent of(boolean isLoggable, String name,
 170                     Level level, ResourceBundle bundle,
 171                     String key, Throwable thrown) {
 172                 LogEvent evt = new LogEvent();
 173                 evt.isLoggable = isLoggable;
 174                 evt.loggerName = name;
 175                 evt.level = level;
 176                 evt.args = null;
 177                 evt.bundle = bundle;
 178                 evt.thrown = thrown;
 179                 evt.supplier = null;
 180                 evt.msg = key;
 181                 return evt;
 182             }
 183 
 184             public static LogEvent of(boolean isLoggable, String name,
 185                     Level level, ResourceBundle bundle,
 186                     String key, Object... params) {
 187                 LogEvent evt = new LogEvent();
 188                 evt.isLoggable = isLoggable;
 189                 evt.loggerName = name;
 190                 evt.level = level;
 191                 evt.args = params;
 192                 evt.bundle = bundle;
 193                 evt.thrown = null;
 194                 evt.supplier = null;
 195                 evt.msg = key;
 196                 return evt;
 197             }
 198 
 199             public static LogEvent of(long sequenceNumber,
 200                     boolean isLoggable, String name,
 201                     Level level, ResourceBundle bundle,
 202                     String key, Supplier<String> supplier,
 203                     Throwable thrown, Object... params) {
 204                 LogEvent evt = new LogEvent(sequenceNumber);
 205                 evt.loggerName = name;
 206                 evt.level = level;
 207                 evt.args = params;
 208                 evt.bundle = bundle;
 209                 evt.thrown = thrown;
 210                 evt.supplier = supplier;
 211                 evt.msg = key;
 212                 evt.isLoggable = isLoggable;
 213                 return evt;
 214             }
 215 
 216         }
 217 
 218         public class LoggerImpl implements Logger {
 219             private final String name;
 220             private Level level = Level.INFO;
 221 
 222             public LoggerImpl(String name) {
 223                 this.name = name;
 224             }
 225 
 226             @Override
 227             public String getName() {
 228                 return name;
 229             }
 230 
 231             @Override
 232             public boolean isLoggable(Level level) {
 233                 return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
 234             }
 235 
 236             @Override
 237             public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
 238                 log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
 239             }
 240 
 241             @Override
 242             public void log(Level level, ResourceBundle bundle, String format, Object... params) {
 243                 log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
 244             }
 245 
 246             void log(LogEvent event) {
 247                 eventQueue.add(event);
 248             }
 249         }
 250 
 251         @Override
 252         public Logger getLogger(String name, Module caller) {
 253             // We should check the permission to obey the API contract, but
 254             // what happens if we don't?
 255             // This is the main difference compared with what we test in
 256             // java/lang/System/LoggerFinder/BaseLoggerFinderTest
 257             SecurityManager sm = System.getSecurityManager();
 258             if (sm != null && doChecks) {
 259                 sm.checkPermission(SimplePolicy.LOGGERFINDER_PERMISSION);
 260             }
 261 
 262             final boolean before = allowAll.get().getAndSet(true);
 263             final ClassLoader callerLoader;
 264             try {
 265                 callerLoader = caller.getClassLoader();
 266             } finally {
 267                 allowAll.get().set(before);
 268             }
 269             if (callerLoader == null) {
 270                 return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
 271             } else {
 272                 return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
 273             }
 274         }
 275     }
 276 
 277     static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
 278 
 279     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
 280 
 281     static void setSecurityManager() {
 282         if (System.getSecurityManager() == null) {
 283             Policy.setPolicy(new SimplePolicy(allowControl, allowAll));
 284             System.setSecurityManager(new SecurityManager());
 285         }
 286     }
 287     public static void main(String[] args) {
 288         if (args.length == 0)
 289             args = new String[] {
 290                 "NOSECURITY",
 291                 "NOPERMISSIONS",
 292                 "WITHPERMISSIONS"
 293             };
 294 
 295         // 1. Obtain destination loggers directly from the LoggerFinder
 296         //   - LoggerFinder.getLogger("foo", type)
 297         BaseLoggerFinder provider =
 298                 BaseLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 299         BaseLoggerFinder.LoggerImpl appSink =
 300                 BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", CustomLoggerTest.class.getModule()));
 301         BaseLoggerFinder.LoggerImpl sysSink =
 302                 BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));
 303 
 304 
 305         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 306             switch (testCase) {
 307                 case NOSECURITY:
 308                     System.out.println("\n*** Without Security Manager\n");
 309                     test(provider, true, appSink, sysSink);
 310                     System.out.println("Tetscase count: " + sequencer.get());
 311                     break;
 312                 case NOPERMISSIONS:
 313                     System.out.println("\n*** With Security Manager, without permissions\n");
 314                     setSecurityManager();
 315                     test(provider, false, appSink, sysSink);
 316                     System.out.println("Tetscase count: " + sequencer.get());
 317                     break;
 318                 case WITHPERMISSIONS:
 319                     System.out.println("\n*** With Security Manager, with control permission\n");
 320                     setSecurityManager();
 321                     final boolean control = allowControl.get().get();
 322                     try {
 323                         allowControl.get().set(true);
 324                         test(provider, true, appSink, sysSink);
 325                     } finally {
 326                         allowControl.get().set(control);
 327                     }
 328                     break;
 329                 default:
 330                     throw new RuntimeException("Unknown test case: " + testCase);
 331             }
 332         });
 333         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 334     }
 335 
 336     public static void test(BaseLoggerFinder provider, boolean hasRequiredPermissions,
 337             BaseLoggerFinder.LoggerImpl appSink, BaseLoggerFinder.LoggerImpl sysSink) {
 338 
 339         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 340         final Map<Logger, String> loggerDescMap = new HashMap<>();
 341 
 342 
 343         // 1. Test loggers returned by:
 344         //   - System.getLogger("foo")
 345         //   - and AccessSystemLogger.getLogger("foo")
 346         Logger appLogger1 = System.getLogger("foo");
 347         loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
 348 
 349         Logger sysLogger1 = null;
 350         try {
 351             sysLogger1 = accessSystemLogger.getLogger("foo");
 352             loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
 353         } catch (AccessControlException acx) {
 354             if (hasRequiredPermissions) {
 355                 throw new RuntimeException("Unexpected security exception: ", acx);
 356             }
 357             if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
 358                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 359             }
 360             throw new RuntimeException("unexpected exception: " + acx, acx);
 361         }
 362 
 363         if (appLogger1 == sysLogger1) {
 364             throw new RuntimeException("identical loggers");
 365         }
 366 
 367         if (provider.system.contains(appLogger1)) {
 368             throw new RuntimeException("app logger in system map");
 369         }
 370         if (provider.user.contains(sysLogger1)) {
 371             throw new RuntimeException("sys logger in appplication map");
 372         }
 373         if (provider.system.contains(sysLogger1)) {
 374             // sysLogger should be a a LazyLoggerWrapper
 375             throw new RuntimeException("sys logger is in system map (should be wrapped)");
 376         }
 377 
 378 
 379         // 2. Test loggers returned by:
 380         //   - System.getLogger(\"foo\", loggerBundle)
 381         //   - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
 382         Logger appLogger2 =
 383                 System.getLogger("foo", loggerBundle);
 384         loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
 385 
 386         Logger sysLogger2 = null;
 387         try {
 388             sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);
 389             loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
 390         } catch (AccessControlException acx) {
 391             if (hasRequiredPermissions) {
 392                 throw new RuntimeException("Unexpected security exception: ", acx);
 393             }
 394             if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
 395                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 396             }
 397             throw new RuntimeException("unexpected exception: " + acx, acx);
 398         }
 399         if (appLogger2 == sysLogger2) {
 400             throw new RuntimeException("identical loggers");
 401         }
 402         if (appLogger2 == appSink) {
 403             throw new RuntimeException("identical loggers");
 404         }
 405         if (sysLogger2 == sysSink) {
 406             throw new RuntimeException("identical loggers");
 407         }
 408 
 409         if (provider.system.contains(appLogger2)) {
 410             throw new RuntimeException("localized app logger in system map");
 411         }
 412         if (provider.user.contains(appLogger2)) {
 413             throw new RuntimeException("localized app logger  in appplication map");
 414         }
 415         if (provider.user.contains(sysLogger2)) {
 416             throw new RuntimeException("localized sys logger in appplication map");
 417         }
 418         if (provider.system.contains(sysLogger2)) {
 419             throw new RuntimeException("localized sys logger not in system map");
 420         }
 421 
 422         testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
 423         testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
 424         testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
 425         testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
 426     }
 427 
 428     public static class Foo {
 429 
 430     }
 431 
 432     static void verbose(String msg) {
 433        if (VERBOSE) {
 434            System.out.println(msg);
 435        }
 436     }
 437 
 438     // Calls the 8 methods defined on Logger and verify the
 439     // parameters received by the underlying BaseLoggerFinder.LoggerImpl
 440     // logger.
 441     private static void testLogger(BaseLoggerFinder provider,
 442             Map<Logger, String> loggerDescMap,
 443             String name,
 444             ResourceBundle loggerBundle,
 445             Logger logger,
 446             BaseLoggerFinder.LoggerImpl sink) {
 447 
 448         System.out.println("Testing " + loggerDescMap.get(logger));
 449 
 450         Foo foo = new Foo();
 451         String fooMsg = foo.toString();
 452         for (Level loggerLevel : Level.values()) {
 453             sink.level = loggerLevel;
 454             for (Level messageLevel : Level.values()) {
 455                 String desc = "logger.log(messageLevel, foo): loggerLevel="
 456                         + loggerLevel+", messageLevel="+messageLevel;
 457                 BaseLoggerFinder.LogEvent expected =
 458                         BaseLoggerFinder.LogEvent.of(
 459                             sequencer.get(),
 460                             messageLevel.compareTo(loggerLevel) >= 0,
 461                             name, messageLevel, (ResourceBundle)null,
 462                             fooMsg, null, (Throwable)null, (Object[])null);
 463                 logger.log(messageLevel, foo);
 464                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 465                     if (provider.eventQueue.poll() != null) {
 466                         throw new RuntimeException("unexpected event in queue for " + desc);
 467                     }
 468                 } else {
 469                     BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 470                     if (!expected.equals(actual)) {
 471                         throw new RuntimeException("mismatch for " + desc
 472                                 + "\n\texpected=" + expected
 473                                 + "\n\t  actual=" + actual);
 474                     } else {
 475                         verbose("Got expected results for "
 476                                 + desc + "\n\t" + expected);
 477                     }
 478                 }
 479             }
 480         }
 481 
 482         String msg = "blah";
 483         for (Level loggerLevel : Level.values()) {
 484             sink.level = loggerLevel;
 485             for (Level messageLevel : Level.values()) {
 486                 String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
 487                         + loggerLevel+", messageLevel="+messageLevel;
 488                 BaseLoggerFinder.LogEvent expected =
 489                         BaseLoggerFinder.LogEvent.of(
 490                             sequencer.get(),
 491                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 492                             name, messageLevel, loggerBundle,
 493                             msg, null, (Throwable)null, (Object[])null);
 494                 logger.log(messageLevel, msg);
 495                 BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 496                 if (!expected.equals(actual)) {
 497                     throw new RuntimeException("mismatch for " + desc
 498                             + "\n\texpected=" + expected
 499                             + "\n\t  actual=" + actual);
 500                 } else {
 501                     verbose("Got expected results for "
 502                             + desc + "\n\t" + expected);
 503                 }
 504             }
 505         }
 506 
 507         Supplier<String> fooSupplier = new Supplier<String>() {
 508             @Override
 509             public String get() {
 510                 return this.toString();
 511             }
 512         };
 513 
 514         for (Level loggerLevel : Level.values()) {
 515             sink.level = loggerLevel;
 516             for (Level messageLevel : Level.values()) {
 517                 String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
 518                         + loggerLevel+", messageLevel="+messageLevel;
 519                 BaseLoggerFinder.LogEvent expected =
 520                         BaseLoggerFinder.LogEvent.of(
 521                             sequencer.get(),
 522                             messageLevel.compareTo(loggerLevel) >= 0,
 523                             name, messageLevel, (ResourceBundle)null,
 524                             fooSupplier.get(), null,
 525                             (Throwable)null, (Object[])null);
 526                 logger.log(messageLevel, fooSupplier);
 527                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 528                     if (provider.eventQueue.poll() != null) {
 529                         throw new RuntimeException("unexpected event in queue for " + desc);
 530                     }
 531                 } else {
 532                     BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 533                     if (!expected.equals(actual)) {
 534                         throw new RuntimeException("mismatch for " + desc
 535                                 + "\n\texpected=" + expected
 536                                 + "\n\t  actual=" + actual);
 537                     } else {
 538                         verbose("Got expected results for "
 539                                 + desc + "\n\t" + expected);
 540                     }
 541                 }
 542             }
 543         }
 544 
 545         String format = "two params [{1} {2}]";
 546         Object arg1 = foo;
 547         Object arg2 = msg;
 548         for (Level loggerLevel : Level.values()) {
 549             sink.level = loggerLevel;
 550             for (Level messageLevel : Level.values()) {
 551                 String desc = "logger.log(messageLevel, format, params...): loggerLevel="
 552                         + loggerLevel+", messageLevel="+messageLevel;
 553                 BaseLoggerFinder.LogEvent expected =
 554                         BaseLoggerFinder.LogEvent.of(
 555                             sequencer.get(),
 556                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 557                             name, messageLevel, loggerBundle,
 558                             format, null, (Throwable)null, new Object[] {arg1, arg2});
 559                 logger.log(messageLevel, format, arg1, arg2);
 560                 BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 561                 if (!expected.equals(actual)) {
 562                     throw new RuntimeException("mismatch for " + desc
 563                             + "\n\texpected=" + expected
 564                             + "\n\t  actual=" + actual);
 565                 } else {
 566                     verbose("Got expected results for "
 567                             + desc + "\n\t" + expected);
 568                 }
 569             }
 570         }
 571 
 572         Throwable thrown = new Exception("OK: log me!");
 573         for (Level loggerLevel : Level.values()) {
 574             sink.level = loggerLevel;
 575             for (Level messageLevel : Level.values()) {
 576                 String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
 577                         + loggerLevel+", messageLevel="+messageLevel;
 578                 BaseLoggerFinder.LogEvent expected =
 579                         BaseLoggerFinder.LogEvent.of(
 580                             sequencer.get(),
 581                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 582                             name, messageLevel, loggerBundle,
 583                             msg, null, thrown, (Object[]) null);
 584                 logger.log(messageLevel, msg, thrown);
 585                 BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 586                 if (!expected.equals(actual)) {
 587                     throw new RuntimeException("mismatch for " + desc
 588                             + "\n\texpected=" + expected
 589                             + "\n\t  actual=" + actual);
 590                 } else {
 591                     verbose("Got expected results for "
 592                             + desc + "\n\t" + expected);
 593                 }
 594             }
 595         }
 596 
 597 
 598         for (Level loggerLevel : Level.values()) {
 599             sink.level = loggerLevel;
 600             for (Level messageLevel : Level.values()) {
 601                 String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
 602                         + loggerLevel+", messageLevel="+messageLevel;
 603                 BaseLoggerFinder.LogEvent expected =
 604                         BaseLoggerFinder.LogEvent.of(
 605                             sequencer.get(),
 606                             messageLevel.compareTo(loggerLevel) >= 0,
 607                             name, messageLevel, (ResourceBundle)null,
 608                             fooSupplier.get(), null,
 609                             (Throwable)thrown, (Object[])null);
 610                 logger.log(messageLevel, fooSupplier, thrown);
 611                 if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 612                     if (provider.eventQueue.poll() != null) {
 613                         throw new RuntimeException("unexpected event in queue for " + desc);
 614                     }
 615                 } else {
 616                     BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 617                     if (!expected.equals(actual)) {
 618                         throw new RuntimeException("mismatch for " + desc
 619                                 + "\n\texpected=" + expected
 620                                 + "\n\t  actual=" + actual);
 621                     } else {
 622                         verbose("Got expected results for "
 623                                 + desc + "\n\t" + expected);
 624                     }
 625                 }
 626             }
 627         }
 628 
 629         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 630         for (Level loggerLevel : Level.values()) {
 631             sink.level = loggerLevel;
 632             for (Level messageLevel : Level.values()) {
 633                 String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
 634                         + loggerLevel+", messageLevel="+messageLevel;
 635                 BaseLoggerFinder.LogEvent expected =
 636                         BaseLoggerFinder.LogEvent.of(
 637                             sequencer.get(),
 638                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 639                             name, messageLevel, bundle,
 640                             format, null, (Throwable)null, new Object[] {foo, msg});
 641                 logger.log(messageLevel, bundle, format, foo, msg);
 642                 BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 643                 if (!expected.equals(actual)) {
 644                     throw new RuntimeException("mismatch for " + desc
 645                             + "\n\texpected=" + expected
 646                             + "\n\t  actual=" + actual);
 647                 } else {
 648                     verbose("Got expected results for "
 649                             + desc + "\n\t" + expected);
 650                 }
 651             }
 652         }
 653 
 654         for (Level loggerLevel : Level.values()) {
 655             sink.level = loggerLevel;
 656             for (Level messageLevel : Level.values()) {
 657                 String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
 658                         + loggerLevel+", messageLevel="+messageLevel;
 659                 BaseLoggerFinder.LogEvent expected =
 660                         BaseLoggerFinder.LogEvent.of(
 661                             sequencer.get(),
 662                             messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
 663                             name, messageLevel, bundle,
 664                             msg, null, thrown, (Object[]) null);
 665                 logger.log(messageLevel, bundle, msg, thrown);
 666                 BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 667                 if (!expected.equals(actual)) {
 668                     throw new RuntimeException("mismatch for " + desc
 669                             + "\n\texpected=" + expected
 670                             + "\n\t  actual=" + actual);
 671                 } else {
 672                     verbose("Got expected results for "
 673                             + desc + "\n\t" + expected);
 674                 }
 675             }
 676         }
 677     }
 678 
 679     final static class PermissionsBuilder {
 680         final Permissions perms;
 681         public PermissionsBuilder() {
 682             this(new Permissions());
 683         }
 684         public PermissionsBuilder(Permissions perms) {
 685             this.perms = perms;
 686         }
 687         public PermissionsBuilder add(Permission p) {
 688             perms.add(p);
 689             return this;
 690         }
 691         public PermissionsBuilder addAll(PermissionCollection col) {
 692             if (col != null) {
 693                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 694                     perms.add(e.nextElement());
 695                 }
 696             }
 697             return this;
 698         }
 699         public Permissions toPermissions() {
 700             final PermissionsBuilder builder = new PermissionsBuilder();
 701             builder.addAll(perms);
 702             return builder.perms;
 703         }
 704     }
 705 
 706     public static class SimplePolicy extends Policy {
 707 
 708         static final RuntimePermission LOGGERFINDER_PERMISSION =
 709                 new RuntimePermission("loggerFinder");
 710         final Permissions permissions;
 711         final Permissions controlPermissions;
 712         final Permissions allPermissions;
 713         final ThreadLocal<AtomicBoolean> allowControl;
 714         final ThreadLocal<AtomicBoolean> allowAll;
 715         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAll) {
 716             this.allowControl = allowControl;
 717             this.allowAll = allowAll;
 718             permissions = new Permissions();
 719 
 720             // these are used for configuring the test itself...
 721             controlPermissions = new Permissions();
 722             controlPermissions.add(LOGGERFINDER_PERMISSION);
 723 
 724             // these are used for simulating a doPrivileged call from
 725             // a class in the BCL
 726             allPermissions = new Permissions();
 727             allPermissions.add(new AllPermission());
 728 
 729         }
 730 
 731         Permissions permissions() {
 732             if (allowAll.get().get()) return allPermissions;
 733             if (allowControl.get().get()) return controlPermissions;
 734             return permissions;
 735 
 736         }
 737 
 738         @Override
 739         public boolean implies(ProtectionDomain domain, Permission permission) {
 740             return permissions().implies(permission);
 741         }
 742 
 743         @Override
 744         public PermissionCollection getPermissions(CodeSource codesource) {
 745             return new PermissionsBuilder().addAll(permissions()).toPermissions();
 746         }
 747 
 748         @Override
 749         public PermissionCollection getPermissions(ProtectionDomain domain) {
 750             return new PermissionsBuilder().addAll(permissions()).toPermissions();
 751         }
 752     }
 753 }