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