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