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