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.io.ByteArrayOutputStream;
  24 import java.io.IOException;
  25 import java.io.PrintStream;
  26 import java.io.UncheckedIOException;
  27 import java.security.AccessControlException;
  28 import java.security.CodeSource;
  29 import java.security.Permission;
  30 import java.security.PermissionCollection;
  31 import java.security.Permissions;
  32 import java.security.Policy;
  33 import java.security.ProtectionDomain;
  34 import java.util.Collections;
  35 import java.util.Enumeration;
  36 import java.util.HashMap;
  37 import java.util.Map;
  38 import java.util.ResourceBundle;
  39 import java.util.stream.Stream;
  40 import java.util.concurrent.ConcurrentHashMap;
  41 import java.util.concurrent.atomic.AtomicBoolean;
  42 import java.util.concurrent.atomic.AtomicLong;
  43 import java.util.function.Supplier;
  44 import java.lang.System.LoggerFinder;
  45 import java.lang.System.Logger;
  46 import java.lang.System.Logger.Level;
  47 import java.lang.reflect.InvocationTargetException;
  48 import java.lang.reflect.Method;
  49 import java.security.AccessController;
  50 import java.security.PrivilegedAction;
  51 import java.util.Locale;
  52 import java.util.Objects;
  53 import java.util.concurrent.atomic.AtomicReference;
  54 import java.util.function.Function;
  55 import jdk.internal.logger.DefaultLoggerFinder;
  56 import jdk.internal.logger.SimpleConsoleLogger;
  57 import sun.util.logging.PlatformLogger;
  58 
  59 /**
  60  * @test
  61  * @bug     8140364 8145686
  62  * @summary JDK implementation specific unit test for the base DefaultLoggerFinder.
  63  *          Tests the behavior of DefaultLoggerFinder and SimpleConsoleLogger
  64  *          implementation.
  65  * @modules java.base/sun.util.logging
  66  *          java.base/jdk.internal.logger
  67  * @build AccessSystemLogger BaseDefaultLoggerFinderTest CustomSystemClassLoader
  68  * @run  driver AccessSystemLogger
  69  * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOSECURITY
  70  * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOPERMISSIONS
  71  * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHPERMISSIONS
  72  * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHCUSTOMWRAPPERS
  73  * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHREFLECTION
  74  * @author danielfuchs
  75  */
  76 public class BaseDefaultLoggerFinderTest {
  77 
  78     static final RuntimePermission LOGGERFINDER_PERMISSION =
  79                 new RuntimePermission("loggerFinder");
  80     final static boolean VERBOSE = false;
  81     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
  82         @Override
  83         protected AtomicBoolean initialValue() {
  84             return  new AtomicBoolean(false);
  85         }
  86     };
  87     static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
  88         @Override
  89         protected AtomicBoolean initialValue() {
  90             return  new AtomicBoolean(false);
  91         }
  92     };
  93 
  94     final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
  95     static final Class<?>[] providerClass;
  96     static {
  97         try {
  98             providerClass = new Class<?>[] {
  99                 ClassLoader.getSystemClassLoader().loadClass("BaseDefaultLoggerFinderTest$BaseLoggerFinder"),
 100             };
 101         } catch (ClassNotFoundException ex) {
 102             throw new ExceptionInInitializerError(ex);
 103         }
 104     }
 105 
 106     /**
 107      * What our test provider needs to implement.
 108      */
 109     public static interface TestLoggerFinder {
 110         public final static AtomicBoolean fails = new AtomicBoolean();
 111         public final static AtomicReference<String> conf = new AtomicReference<>("");
 112         public final static AtomicLong sequencer = new AtomicLong();
 113 
 114 
 115         public Logger getLogger(String name, Module caller);
 116         public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
 117         void setLevel(Logger logger, Level level, Module caller);
 118         void setLevel(Logger logger, PlatformLogger.Level level, Module caller);
 119         PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger);
 120     }
 121 
 122     public static class BaseLoggerFinder extends DefaultLoggerFinder implements TestLoggerFinder {
 123 
 124         static final RuntimePermission LOGGERFINDER_PERMISSION =
 125                     new RuntimePermission("loggerFinder");
 126         public BaseLoggerFinder() {
 127             if (fails.get()) {
 128                 throw new RuntimeException("Simulate exception while loading provider");
 129             }
 130         }
 131 
 132         @Override
 133         public void setLevel(Logger logger, Level level, Module caller) {
 134             PrivilegedAction<Void> pa = () -> {
 135                 setLevel(logger, PlatformLogger.toPlatformLevel(level), caller);
 136                 return null;
 137             };
 138             AccessController.doPrivileged(pa);
 139         }
 140 
 141         @Override
 142         public void setLevel(Logger logger, PlatformLogger.Level level, Module caller) {
 143             PrivilegedAction<Logger> pa = () -> demandLoggerFor(logger.getName(), caller);
 144             Logger impl = AccessController.doPrivileged(pa);
 145             SimpleConsoleLogger.class.cast(impl)
 146                     .getLoggerConfiguration()
 147                     .setPlatformLevel(level);
 148         }
 149 
 150         @Override
 151         public PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger) {
 152             PrivilegedAction<PlatformLogger.Bridge> pa = () ->
 153                 PlatformLogger.Bridge.convert(logger);
 154             return AccessController.doPrivileged(pa);
 155         }
 156 
 157     }
 158 
 159     public static class MyBundle extends ResourceBundle {
 160 
 161         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
 162 
 163         @Override
 164         protected Object handleGetObject(String key) {
 165             if (key.contains(" (translated)")) {
 166                 throw new RuntimeException("Unexpected key: " + key);
 167             }
 168             return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
 169         }
 170 
 171         @Override
 172         public Enumeration<String> getKeys() {
 173             return Collections.enumeration(map.keySet());
 174         }
 175 
 176     }
 177     public static class MyLoggerBundle extends MyBundle {
 178 
 179     }
 180 
 181     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS,
 182                            WITHCUSTOMWRAPPERS, WITHREFLECTION};
 183 
 184     static void setSecurityManager() {
 185         if (System.getSecurityManager() == null) {
 186             Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
 187             System.setSecurityManager(new SecurityManager());
 188         }
 189     }
 190 
 191     static TestLoggerFinder getLoggerFinder(Class<?> expectedClass) {
 192         LoggerFinder provider = null;
 193         try {
 194             TestLoggerFinder.sequencer.incrementAndGet();
 195             provider = LoggerFinder.getLoggerFinder();
 196         } catch(AccessControlException a) {
 197             throw a;
 198         }
 199         ErrorStream.errorStream.store();
 200         System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
 201         expectedClass.cast(provider);
 202         return TestLoggerFinder.class.cast(provider);
 203     }
 204 
 205 
 206     static class ErrorStream extends PrintStream {
 207 
 208         static AtomicBoolean forward = new AtomicBoolean();
 209         ByteArrayOutputStream out;
 210         String saved = "";
 211         public ErrorStream(ByteArrayOutputStream out) {
 212             super(out);
 213             this.out = out;
 214         }
 215 
 216         @Override
 217         public void write(int b) {
 218             super.write(b);
 219             if (forward.get()) err.write(b);
 220         }
 221 
 222         @Override
 223         public void write(byte[] b) throws IOException {
 224             super.write(b);
 225             if (forward.get()) err.write(b);
 226         }
 227 
 228         @Override
 229         public void write(byte[] buf, int off, int len) {
 230             super.write(buf, off, len);
 231             if (forward.get()) err.write(buf, off, len);
 232         }
 233 
 234         public String peek() {
 235             flush();
 236             return out.toString();
 237         }
 238 
 239         public String drain() {
 240             flush();
 241             String res = out.toString();
 242             out.reset();
 243             return res;
 244         }
 245 
 246         public void store() {
 247             flush();
 248             saved = out.toString();
 249             out.reset();
 250         }
 251 
 252         public void restore() {
 253             out.reset();
 254             try {
 255                 out.write(saved.getBytes());
 256             } catch(IOException io) {
 257                 throw new UncheckedIOException(io);
 258             }
 259         }
 260 
 261         static final PrintStream err = System.err;
 262         static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
 263     }
 264 
 265     private static StringBuilder appendProperty(StringBuilder b, String name) {
 266         String value = System.getProperty(name);
 267         if (value == null) return b;
 268         return b.append(name).append("=").append(value).append('\n');
 269     }
 270 
 271     static class CustomLoggerWrapper implements Logger {
 272 
 273         Logger impl;
 274         public CustomLoggerWrapper(Logger logger) {
 275             this.impl = Objects.requireNonNull(logger);
 276         }
 277 
 278 
 279         @Override
 280         public String getName() {
 281             return impl.getName();
 282         }
 283 
 284         @Override
 285         public boolean isLoggable(Level level) {
 286             return impl.isLoggable(level);
 287         }
 288 
 289         @Override
 290         public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
 291             impl.log(level, rb, string, thrwbl);
 292         }
 293 
 294         @Override
 295         public void log(Level level, ResourceBundle rb, String string, Object... os) {
 296             impl.log(level, rb, string, os);
 297         }
 298 
 299         @Override
 300         public void log(Level level, Object o) {
 301             impl.log(level, o);
 302         }
 303 
 304         @Override
 305         public void log(Level level, String string) {
 306             impl.log(level, string);
 307         }
 308 
 309         @Override
 310         public void log(Level level, Supplier<String> splr) {
 311             impl.log(level, splr);
 312         }
 313 
 314         @Override
 315         public void log(Level level, String string, Object... os) {
 316            impl.log(level, string, os);
 317         }
 318 
 319         @Override
 320         public void log(Level level, String string, Throwable thrwbl) {
 321             impl.log(level, string, thrwbl);
 322         }
 323 
 324         @Override
 325         public void log(Level level, Supplier<String> splr, Throwable thrwbl) {
 326             Logger.super.log(level, splr, thrwbl);
 327         }
 328 
 329         @Override
 330         public String toString() {
 331             return super.toString() + "(impl=" + impl + ")";
 332         }
 333 
 334     }
 335     /**
 336      * The ReflectionLoggerWrapper additionally makes it possible to verify
 337      * that code which use reflection to call System.Logger will be skipped
 338      * when looking for the calling method.
 339      */
 340     static class ReflectionLoggerWrapper implements Logger {
 341 
 342         Logger impl;
 343         public ReflectionLoggerWrapper(Logger logger) {
 344             this.impl = Objects.requireNonNull(logger);
 345         }
 346 
 347         private Object invoke(Method m, Object... params) {
 348             try {
 349                 return m.invoke(impl, params);
 350             } catch (IllegalAccessException | IllegalArgumentException
 351                     | InvocationTargetException ex) {
 352                 throw new RuntimeException(ex);
 353             }
 354         }
 355 
 356         @Override
 357         public String getName() {
 358             return impl.getName();
 359         }
 360 
 361         @Override
 362         public boolean isLoggable(Level level) {
 363             return impl.isLoggable(level);
 364         }
 365 
 366         @Override
 367         public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
 368             try {
 369                 invoke(System.Logger.class.getMethod(
 370                         "log", Level.class, ResourceBundle.class, String.class, Throwable.class),
 371                         level, rb, string, thrwbl);
 372             } catch (NoSuchMethodException ex) {
 373                 throw new RuntimeException(ex);
 374             }
 375         }
 376 
 377         @Override
 378         public void log(Level level, ResourceBundle rb, String string, Object... os) {
 379             try {
 380                 invoke(System.Logger.class.getMethod(
 381                         "log", Level.class, ResourceBundle.class, String.class, Object[].class),
 382                         level, rb, string, os);
 383             } catch (NoSuchMethodException ex) {
 384                 throw new RuntimeException(ex);
 385             }
 386         }
 387 
 388         @Override
 389         public void log(Level level, String string) {
 390             try {
 391                 invoke(System.Logger.class.getMethod(
 392                         "log", Level.class, String.class),
 393                         level, string);
 394             } catch (NoSuchMethodException ex) {
 395                 throw new RuntimeException(ex);
 396             }
 397         }
 398 
 399         @Override
 400         public void log(Level level, String string, Object... os) {
 401             try {
 402                 invoke(System.Logger.class.getMethod(
 403                         "log", Level.class, String.class, Object[].class),
 404                         level, string, os);
 405             } catch (NoSuchMethodException ex) {
 406                 throw new RuntimeException(ex);
 407             }
 408         }
 409 
 410         @Override
 411         public void log(Level level, String string, Throwable thrwbl) {
 412             try {
 413                 invoke(System.Logger.class.getMethod(
 414                         "log", Level.class, String.class, Throwable.class),
 415                         level, string, thrwbl);
 416             } catch (NoSuchMethodException ex) {
 417                 throw new RuntimeException(ex);
 418             }
 419         }
 420 
 421 
 422         @Override
 423         public String toString() {
 424             return super.toString() + "(impl=" + impl + ")";
 425         }
 426 
 427     }
 428 
 429 
 430     public static void main(String[] args) {
 431         if (args.length == 0) {
 432             args = new String[] {
 433                 //"NOSECURITY",
 434                 "NOPERMISSIONS",
 435                 "WITHPERMISSIONS",
 436                 "WITHCUSTOMWRAPPERS",
 437                 "WITHREFLECTION"
 438             };
 439         }
 440         Locale.setDefault(Locale.ENGLISH);
 441         System.setErr(ErrorStream.errorStream);
 442         //System.setProperty("jdk.logger.finder.error", "ERROR");
 443         //System.setProperty("jdk.logger.finder.singleton", "true");
 444         //System.setProperty("test.fails", "true");
 445         TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
 446         StringBuilder c = new StringBuilder();
 447         appendProperty(c, "jdk.logger.packages");
 448         appendProperty(c, "jdk.logger.finder.error");
 449         appendProperty(c, "jdk.logger.finder.singleton");
 450         appendProperty(c, "test.fails");
 451         TestLoggerFinder.conf.set(c.toString());
 452         try {
 453             test(args);
 454         } finally {
 455             try {
 456                 System.setErr(ErrorStream.err);
 457             } catch (Error | RuntimeException x) {
 458                 x.printStackTrace(ErrorStream.err);
 459             }
 460         }
 461     }
 462 
 463 
 464     public static void test(String[] args) {
 465 
 466         final Class<?> expectedClass = jdk.internal.logger.DefaultLoggerFinder.class;
 467 
 468         System.out.println("Declared provider class: " + providerClass[0]
 469                 + "[" + providerClass[0].getClassLoader() + "]");
 470 
 471         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 472             TestLoggerFinder provider;
 473             ErrorStream.errorStream.restore();
 474             switch (testCase) {
 475                 case NOSECURITY:
 476                     System.out.println("\n*** Without Security Manager\n");
 477                     System.out.println(TestLoggerFinder.conf.get());
 478                     provider = getLoggerFinder(expectedClass);
 479                     if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
 480                         throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
 481                     }
 482                     test(provider, true);
 483                     System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
 484                     break;
 485                 case NOPERMISSIONS:
 486                     System.out.println("\n*** With Security Manager, without permissions\n");
 487                     System.out.println(TestLoggerFinder.conf.get());
 488                     setSecurityManager();
 489                     try {
 490                         provider = getLoggerFinder(expectedClass);
 491                         throw new RuntimeException("Expected exception not raised");
 492                     } catch (AccessControlException x) {
 493                         if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
 494                             throw new RuntimeException("Unexpected permission check", x);
 495                         }
 496                         final boolean control = allowControl.get().get();
 497                         try {
 498                             allowControl.get().set(true);
 499                             provider = getLoggerFinder(expectedClass);
 500                             if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
 501                                 throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
 502                             }
 503                         } finally {
 504                             allowControl.get().set(control);
 505                         }
 506                     }
 507                     test(provider, false);
 508                     System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
 509                     break;
 510                 case WITHPERMISSIONS:
 511                     System.out.println("\n*** With Security Manager, with control permission\n");
 512                     System.out.println(TestLoggerFinder.conf.get());
 513                     setSecurityManager();
 514                     final boolean control = allowControl.get().get();
 515                     try {
 516                         allowControl.get().set(true);
 517                         provider = getLoggerFinder(expectedClass);
 518                         if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
 519                             throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
 520                         }
 521                         test(provider, true);
 522                     } finally {
 523                         allowControl.get().set(control);
 524                     }
 525                     break;
 526                 case WITHCUSTOMWRAPPERS:
 527                     System.out.println("\n*** With Security Manager, with control permission and custom Wrapper\n");
 528                     System.out.println(TestLoggerFinder.conf.get());
 529                     setSecurityManager();
 530                     final boolean previous = allowControl.get().get();
 531                     try {
 532                         allowControl.get().set(true);
 533                         provider = getLoggerFinder(expectedClass);
 534                         if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
 535                             throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
 536                         }
 537                         test(provider, CustomLoggerWrapper::new, true);
 538                     } finally {
 539                         allowControl.get().set(previous);
 540                     }
 541                     break;
 542                 case WITHREFLECTION:
 543                     System.out.println("\n*** With Security Manager,"
 544                             + " with control permission,"
 545                             + " using reflection while logging\n");
 546                     System.out.println(TestLoggerFinder.conf.get());
 547                     setSecurityManager();
 548                     final boolean before = allowControl.get().get();
 549                     try {
 550                         allowControl.get().set(true);
 551                         provider = getLoggerFinder(expectedClass);
 552                         if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
 553                             throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
 554                         }
 555                         test(provider, ReflectionLoggerWrapper::new, true);
 556                     } finally {
 557                         allowControl.get().set(before);
 558                     }
 559                     break;
 560                 default:
 561                     throw new RuntimeException("Unknown test case: " + testCase);
 562             }
 563         });
 564         System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
 565     }
 566 
 567     public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
 568         test(provider, Function.identity(), hasRequiredPermissions);
 569     }
 570 
 571     public static void test(TestLoggerFinder provider, Function<Logger, Logger> wrapper, boolean hasRequiredPermissions) {
 572 
 573         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 574         final Map<Logger, String> loggerDescMap = new HashMap<>();
 575 
 576         System.Logger sysLogger = wrapper.apply(accessSystemLogger.getLogger("foo"));
 577         loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
 578         System.Logger localizedSysLogger = wrapper.apply(accessSystemLogger.getLogger("fox", loggerBundle));
 579         loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
 580         System.Logger appLogger = wrapper.apply(System.getLogger("bar"));
 581         loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
 582         System.Logger localizedAppLogger = wrapper.apply(System.getLogger("baz", loggerBundle));
 583         loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
 584 
 585         testLogger(provider, loggerDescMap, "foo", null, sysLogger, accessSystemLogger.getClass());
 586         testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger, accessSystemLogger.getClass());
 587         testLogger(provider, loggerDescMap, "foo", null, appLogger, BaseDefaultLoggerFinderTest.class);
 588         testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger, BaseDefaultLoggerFinderTest.class);
 589     }
 590 
 591     public static class Foo {
 592 
 593     }
 594 
 595     static void verbose(String msg) {
 596        if (VERBOSE) {
 597            System.out.println(msg);
 598        }
 599     }
 600 
 601     // Calls the 8 methods defined on Logger and verify the
 602     // parameters received by the underlying TestProvider.LoggerImpl
 603     // logger.
 604     private static void testLogger(TestLoggerFinder provider,
 605             Map<Logger, String> loggerDescMap,
 606             String name,
 607             ResourceBundle loggerBundle,
 608             Logger logger,
 609             Class<?> callerClass) {
 610 
 611         System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
 612         AtomicLong sequencer = TestLoggerFinder.sequencer;
 613 
 614         Module caller = callerClass.getModule();
 615         Foo foo = new Foo();
 616         String fooMsg = foo.toString();
 617         for (Level loggerLevel : Level.values()) {
 618             provider.setLevel(logger, loggerLevel, caller);
 619             for (Level messageLevel : Level.values()) {
 620                 ErrorStream.errorStream.drain();
 621                 String desc = "logger.log(messageLevel, foo): loggerLevel="
 622                         + loggerLevel+", messageLevel="+messageLevel;
 623                 sequencer.incrementAndGet();
 624                 logger.log(messageLevel, foo);
 625                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 626                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 627                         throw new RuntimeException("unexpected event in queue for "
 628                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 629                     }
 630                 } else {
 631                     String logged = ErrorStream.errorStream.drain();
 632                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 633                         || !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
 634                         throw new RuntimeException("mismatch for " + desc
 635                                 + "\n\texpected:" + "\n<<<<\n"
 636                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 637                                 + messageLevel.getName() + " " + fooMsg
 638                                 + "\n>>>>"
 639                                 + "\n\t  actual:"
 640                                 + "\n<<<<\n" + logged + ">>>>\n");
 641                     } else {
 642                         verbose("Got expected results for "
 643                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 644                     }
 645                 }
 646             }
 647         }
 648 
 649         String msg = "blah";
 650         for (Level loggerLevel : Level.values()) {
 651             provider.setLevel(logger, loggerLevel, caller);
 652             for (Level messageLevel : Level.values()) {
 653                 String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
 654                         + loggerLevel+", messageLevel="+messageLevel;
 655                 sequencer.incrementAndGet();
 656                 logger.log(messageLevel, msg);
 657                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 658                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 659                         throw new RuntimeException("unexpected event in queue for "
 660                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 661                     }
 662                 } else {
 663                     String logged = ErrorStream.errorStream.drain();
 664                     String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
 665                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 666                         || !logged.contains(messageLevel.getName() + ": " + msgText)) {
 667                         throw new RuntimeException("mismatch for " + desc
 668                                 + "\n\texpected:" + "\n<<<<\n"
 669                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 670                                 + messageLevel.getName() + " " + msgText
 671                                 + "\n>>>>"
 672                                 + "\n\t  actual:"
 673                                 + "\n<<<<\n" + logged + ">>>>\n");
 674                     } else {
 675                         verbose("Got expected results for "
 676                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 677                     }
 678                 }
 679             }
 680         }
 681 
 682         Supplier<String> fooSupplier = new Supplier<String>() {
 683             @Override
 684             public String get() {
 685                 return this.toString();
 686             }
 687         };
 688 
 689         for (Level loggerLevel : Level.values()) {
 690             provider.setLevel(logger, loggerLevel, caller);
 691             for (Level messageLevel : Level.values()) {
 692                 String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
 693                         + loggerLevel+", messageLevel="+messageLevel;
 694                 sequencer.incrementAndGet();
 695                 logger.log(messageLevel, fooSupplier);
 696                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 697                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 698                         throw new RuntimeException("unexpected event in queue for "
 699                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 700                     }
 701                 } else {
 702                     String logged = ErrorStream.errorStream.drain();
 703                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 704                         || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
 705                         throw new RuntimeException("mismatch for " + desc
 706                                 + "\n\texpected:" + "\n<<<<\n"
 707                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 708                                 + messageLevel.getName() + " " + fooSupplier.get()
 709                                 + "\n>>>>"
 710                                 + "\n\t  actual:"
 711                                 + "\n<<<<\n" + logged + ">>>>\n");
 712                     } else {
 713                         verbose("Got expected results for "
 714                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 715                     }
 716                 }
 717             }
 718         }
 719 
 720 
 721         String format = "two params [{1} {2}]";
 722         Object arg1 = foo;
 723         Object arg2 = msg;
 724         for (Level loggerLevel : Level.values()) {
 725             provider.setLevel(logger, loggerLevel, caller);
 726             for (Level messageLevel : Level.values()) {
 727                 String desc = "logger.log(messageLevel, format, params...): loggerLevel="
 728                         + loggerLevel+", messageLevel="+messageLevel;
 729                 sequencer.incrementAndGet();
 730                 logger.log(messageLevel, format, foo, msg);
 731                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 732                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 733                         throw new RuntimeException("unexpected event in queue for "
 734                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 735                     }
 736                 } else {
 737                     String logged = ErrorStream.errorStream.drain();
 738                     String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
 739                     String text = java.text.MessageFormat.format(msgFormat, foo, msg);
 740                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 741                         || !logged.contains(messageLevel.getName() + ": " + text)) {
 742                         throw new RuntimeException("mismatch for " + desc
 743                                 + "\n\texpected:" + "\n<<<<\n"
 744                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 745                                 + messageLevel.getName() + " " + text
 746                                 + "\n>>>>"
 747                                 + "\n\t  actual:"
 748                                 + "\n<<<<\n" + logged + ">>>>\n");
 749                     } else {
 750                         verbose("Got expected results for "
 751                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 752                     }
 753                 }
 754             }
 755         }
 756 
 757         Throwable thrown = new Exception("OK: log me!");
 758         for (Level loggerLevel : Level.values()) {
 759             provider.setLevel(logger, loggerLevel, caller);
 760             for (Level messageLevel : Level.values()) {
 761                 String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
 762                         + loggerLevel+", messageLevel="+messageLevel;
 763                 sequencer.incrementAndGet();
 764                 logger.log(messageLevel, msg, thrown);
 765                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 766                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 767                         throw new RuntimeException("unexpected event in queue for "
 768                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 769                     }
 770                 } else {
 771                     String logged = ErrorStream.errorStream.drain();
 772                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 773                     thrown.printStackTrace(new PrintStream(baos));
 774                     String text = baos.toString();
 775                     String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
 776                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 777                         || !logged.contains(messageLevel.getName() + ": " + msgText)
 778                         || !logged.contains(text)) {
 779                         throw new RuntimeException("mismatch for " + desc
 780                                 + "\n\texpected:" + "\n<<<<\n"
 781                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 782                                 + messageLevel.getName() + " " + msgText +"\n"
 783                                 + text
 784                                 + ">>>>"
 785                                 + "\n\t  actual:"
 786                                 + "\n<<<<\n" + logged + ">>>>\n");
 787                     } else {
 788                         verbose("Got expected results for "
 789                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 790                     }
 791                 }
 792             }
 793         }
 794 
 795 
 796         for (Level loggerLevel : Level.values()) {
 797             provider.setLevel(logger, loggerLevel, caller);
 798             for (Level messageLevel : Level.values()) {
 799                 String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
 800                         + loggerLevel+", messageLevel="+messageLevel;
 801                 sequencer.incrementAndGet();
 802                 logger.log(messageLevel, fooSupplier, thrown);
 803                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 804                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 805                         throw new RuntimeException("unexpected event in queue for "
 806                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 807                     }
 808                 } else {
 809                     String logged = ErrorStream.errorStream.drain();
 810                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 811                     thrown.printStackTrace(new PrintStream(baos));
 812                     String text = baos.toString();
 813                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 814                         || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
 815                         || !logged.contains(text)) {
 816                         throw new RuntimeException("mismatch for " + desc
 817                                 + "\n\texpected:" + "\n<<<<\n"
 818                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 819                                 + messageLevel.getName() + " " + fooSupplier.get() +"\n"
 820                                 + text
 821                                 + ">>>>"
 822                                 + "\n\t  actual:"
 823                                 + "\n<<<<\n" + logged + ">>>>\n");
 824                     } else {
 825                         verbose("Got expected results for "
 826                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 827                     }
 828                 }
 829             }
 830         }
 831 
 832         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 833         for (Level loggerLevel : Level.values()) {
 834             provider.setLevel(logger, loggerLevel, caller);
 835             for (Level messageLevel : Level.values()) {
 836                 String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
 837                         + loggerLevel+", messageLevel="+messageLevel;
 838                 sequencer.incrementAndGet();
 839                 logger.log(messageLevel, bundle, format, foo, msg);
 840                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 841                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 842                         throw new RuntimeException("unexpected event in queue for "
 843                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 844                     }
 845                 } else {
 846                     String logged = ErrorStream.errorStream.drain();
 847                     String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
 848                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 849                         || !logged.contains(messageLevel.getName() + ": " + text)) {
 850                         throw new RuntimeException("mismatch for " + desc
 851                                 + "\n\texpected:" + "\n<<<<\n"
 852                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 853                                 + messageLevel.getName() + " " + text
 854                                 + "\n>>>>"
 855                                 + "\n\t  actual:"
 856                                 + "\n<<<<\n" + logged + ">>>>\n");
 857                     } else {
 858                         verbose("Got expected results for "
 859                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 860                     }
 861                 }
 862             }
 863         }
 864 
 865         for (Level loggerLevel : Level.values()) {
 866             provider.setLevel(logger, loggerLevel, caller);
 867             for (Level messageLevel : Level.values()) {
 868                 String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
 869                         + loggerLevel+", messageLevel="+messageLevel;
 870                 sequencer.incrementAndGet();
 871                 logger.log(messageLevel, bundle, msg, thrown);
 872                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 873                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 874                         throw new RuntimeException("unexpected event in queue for "
 875                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 876                     }
 877                 } else {
 878                     String logged = ErrorStream.errorStream.drain();
 879                     String textMsg = bundle.getString(msg);
 880                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 881                     thrown.printStackTrace(new PrintStream(baos));
 882                     String text = baos.toString();
 883                     if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
 884                         || !logged.contains(messageLevel.getName() + ": " + textMsg)
 885                         || !logged.contains(text)) {
 886                         throw new RuntimeException("mismatch for " + desc
 887                                 + "\n\texpected:" + "\n<<<<\n"
 888                                 + "[date] BaseDefaultLoggerFinderTest testLogger\n"
 889                                 + messageLevel.getName() + " " + textMsg +"\n"
 890                                 + text
 891                                 + ">>>>"
 892                                 + "\n\t  actual:"
 893                                 + "\n<<<<\n" + logged + ">>>>\n");
 894                     } else {
 895                         verbose("Got expected results for "
 896                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 897                     }
 898                 }
 899             }
 900         }
 901 
 902     }
 903 
 904     final static class PermissionsBuilder {
 905         final Permissions perms;
 906         public PermissionsBuilder() {
 907             this(new Permissions());
 908         }
 909         public PermissionsBuilder(Permissions perms) {
 910             this.perms = perms;
 911         }
 912         public PermissionsBuilder add(Permission p) {
 913             perms.add(p);
 914             return this;
 915         }
 916         public PermissionsBuilder addAll(PermissionCollection col) {
 917             if (col != null) {
 918                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 919                     perms.add(e.nextElement());
 920                 }
 921             }
 922             return this;
 923         }
 924         public Permissions toPermissions() {
 925             final PermissionsBuilder builder = new PermissionsBuilder();
 926             builder.addAll(perms);
 927             return builder.perms;
 928         }
 929     }
 930 
 931     public static class SimplePolicy extends Policy {
 932         final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
 933         final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
 934 
 935         final Permissions permissions;
 936         final ThreadLocal<AtomicBoolean> allowControl;
 937         final ThreadLocal<AtomicBoolean> allowAccess;
 938         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
 939             this.allowControl = allowControl;
 940             this.allowAccess = allowAccess;
 941             permissions = new Permissions();
 942             permissions.add(new RuntimePermission("setIO"));
 943         }
 944 
 945         Permissions getPermissions() {
 946             if (allowControl.get().get() || allowAccess.get().get()) {
 947                 PermissionsBuilder builder =  new PermissionsBuilder()
 948                         .addAll(permissions);
 949                 if (allowControl.get().get()) {
 950                     builder.add(CONTROL);
 951                 }
 952                 if (allowAccess.get().get()) {
 953                     builder.add(ACCESS);
 954                 }
 955                 return builder.toPermissions();
 956             }
 957             return permissions;
 958         }
 959 
 960         @Override
 961         public boolean implies(ProtectionDomain domain, Permission permission) {
 962             return getPermissions().implies(permission);
 963         }
 964 
 965         @Override
 966         public PermissionCollection getPermissions(CodeSource codesource) {
 967             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
 968         }
 969 
 970         @Override
 971         public PermissionCollection getPermissions(ProtectionDomain domain) {
 972             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
 973         }
 974     }
 975 }