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