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