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