1 /*
   2  * Copyright (c) 2015, 2018, 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 BaseLoggerFinder BaseLoggerFinder2
  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("BaseLoggerFinder"),
 122                 ClassLoader.getSystemClassLoader().loadClass("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 MyBundle extends ResourceBundle {
 175 
 176         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
 177 
 178         @Override
 179         protected Object handleGetObject(String key) {
 180             if (key.contains(" (translated)")) {
 181                 throw new RuntimeException("Unexpected key: " + key);
 182             }
 183             return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
 184         }
 185 
 186         @Override
 187         public Enumeration<String> getKeys() {
 188             return Collections.enumeration(map.keySet());
 189         }
 190 
 191     }
 192     public static class MyLoggerBundle extends MyBundle {
 193 
 194     }
 195 
 196     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
 197 
 198     static void setSecurityManager() {
 199         if (System.getSecurityManager() == null) {
 200             Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
 201             System.setSecurityManager(new SecurityManager());
 202         }
 203     }
 204 
 205     static LoggerFinder getLoggerFinder(Class<?> expectedClass,
 206             String errorPolicy, boolean singleton) {
 207         LoggerFinder provider = null;
 208         try {
 209             TestLoggerFinder.sequencer.incrementAndGet();
 210             provider = LoggerFinder.getLoggerFinder();
 211             if (TestLoggerFinder.fails.get() || singleton) {
 212                 if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
 213                     throw new RuntimeException("Expected exception not thrown");
 214                 } else if ("WARNING".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
 215                     String warning = ErrorStream.errorStream.peek();
 216                     if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
 217                         throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
 218                     }
 219                 } else if ("DEBUG".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
 220                     String warning = ErrorStream.errorStream.peek();
 221                     if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
 222                         throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
 223                     }
 224                     if (!warning.contains("WARNING: Exception raised trying to instantiate LoggerFinder")) {
 225                         throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
 226                     }
 227                     if (TestLoggerFinder.fails.get()) {
 228                         if (!warning.contains("java.util.ServiceConfigurationError: java.lang.System$LoggerFinder: Provider BaseLoggerFinder could not be instantiated")) {
 229                             throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
 230                         }
 231                     } else if (singleton) {
 232                         if (!warning.contains("java.util.ServiceConfigurationError: More than on LoggerFinder implementation")) {
 233                             throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
 234                         }
 235                     }
 236                 } else if ("QUIET".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
 237                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 238                         throw new RuntimeException("Unexpected error message found: "
 239                                 + ErrorStream.errorStream.peek());
 240                     }
 241                 }
 242             }
 243         } catch(AccessControlException a) {
 244             throw a;
 245         } catch(Throwable t) {
 246             if (TestLoggerFinder.fails.get() || singleton) {
 247                 // must check System.err
 248                 if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
 249                     provider = LoggerFinder.getLoggerFinder();
 250                 } else {
 251                     Throwable orig = t.getCause();
 252                     while (orig != null && orig.getCause() != null) orig = orig.getCause();
 253                     if (orig != null) orig.printStackTrace(ErrorStream.err);
 254                     throw new RuntimeException("Unexpected exception: " + t, t);
 255                 }
 256             } else {
 257                 throw new RuntimeException("Unexpected exception: " + t, t);
 258             }
 259         }
 260         expectedClass.cast(provider);
 261         ErrorStream.errorStream.store();
 262         System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
 263         return provider;
 264     }
 265 
 266 
 267     static class ErrorStream extends PrintStream {
 268 
 269         static AtomicBoolean forward = new AtomicBoolean();
 270         ByteArrayOutputStream out;
 271         String saved = "";
 272         public ErrorStream(ByteArrayOutputStream out) {
 273             super(out);
 274             this.out = out;
 275         }
 276 
 277         @Override
 278         public void write(int b) {
 279             super.write(b);
 280             if (forward.get()) err.write(b);
 281         }
 282 
 283         @Override
 284         public void write(byte[] b) throws IOException {
 285             super.write(b);
 286             if (forward.get()) err.write(b);
 287         }
 288 
 289         @Override
 290         public void write(byte[] buf, int off, int len) {
 291             super.write(buf, off, len);
 292             if (forward.get()) err.write(buf, off, len);
 293         }
 294 
 295         public String peek() {
 296             flush();
 297             return out.toString();
 298         }
 299 
 300         public String drain() {
 301             flush();
 302             String res = out.toString();
 303             out.reset();
 304             return res;
 305         }
 306 
 307         public void store() {
 308             flush();
 309             saved = out.toString();
 310             out.reset();
 311         }
 312 
 313         public void restore() {
 314             out.reset();
 315             try {
 316                 out.write(saved.getBytes());
 317             } catch(IOException io) {
 318                 throw new UncheckedIOException(io);
 319             }
 320         }
 321 
 322         static final PrintStream err = System.err;
 323         static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
 324     }
 325 
 326     private static StringBuilder appendProperty(StringBuilder b, String name) {
 327         String value = System.getProperty(name);
 328         if (value == null) return b;
 329         return b.append(name).append("=").append(value).append('\n');
 330     }
 331 
 332     public static void main(String[] args) {
 333         if (args.length == 0) {
 334             args = new String[] {
 335                 "NOSECURITY",
 336                 "NOPERMISSIONS",
 337                 "WITHPERMISSIONS"
 338             };
 339         }
 340         Locale.setDefault(Locale.ENGLISH);
 341         System.setErr(ErrorStream.errorStream);
 342         System.setProperty("jdk.logger.packages", TestLoggerFinder.LoggerImpl.class.getName());
 343         //System.setProperty("jdk.logger.finder.error", "ERROR");
 344         //System.setProperty("jdk.logger.finder.singleton", "true");
 345         //System.setProperty("test.fails", "true");
 346         TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
 347         StringBuilder c = new StringBuilder();
 348         appendProperty(c, "jdk.logger.packages");
 349         appendProperty(c, "jdk.logger.finder.error");
 350         appendProperty(c, "jdk.logger.finder.singleton");
 351         appendProperty(c, "test.fails");
 352         TestLoggerFinder.conf.set(c.toString());
 353         try {
 354             test(args);
 355         } finally {
 356             try {
 357                 System.setErr(ErrorStream.err);
 358             } catch (Error | RuntimeException x) {
 359                 x.printStackTrace(ErrorStream.err);
 360             }
 361         }
 362     }
 363 
 364 
 365     public static void test(String[] args) {
 366 
 367         final String errorPolicy =  System.getProperty("jdk.logger.finder.error", "WARNING");
 368         final Boolean ensureSingleton = Boolean.getBoolean("jdk.logger.finder.singleton");
 369 
 370         final Class<?> expectedClass =
 371                 TestLoggerFinder.fails.get() || ensureSingleton
 372                 ? jdk.internal.logger.DefaultLoggerFinder.class
 373                 : TestLoggerFinder.class;
 374 
 375         System.out.println("Declared provider class: " + providerClass[0]
 376                 + "[" + providerClass[0].getClassLoader() + "]");
 377 
 378         if (!TestLoggerFinder.fails.get()) {
 379             ServiceLoader<LoggerFinder> serviceLoader =
 380                 ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader());
 381             Iterator<LoggerFinder> iterator = serviceLoader.iterator();
 382             Object firstProvider = iterator.next();
 383             if (!firstProvider.getClass().getName().equals("BaseLoggerFinder")) {
 384                 throw new RuntimeException("Unexpected provider: " + firstProvider.getClass().getName());
 385             }
 386             if (!iterator.hasNext()) {
 387                 throw new RuntimeException("Expected two providers");
 388             }
 389         }
 390 
 391         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 392             LoggerFinder provider;
 393             ErrorStream.errorStream.restore();
 394             switch (testCase) {
 395                 case NOSECURITY:
 396                     System.out.println("\n*** Without Security Manager\n");
 397                     System.out.println(TestLoggerFinder.conf.get());
 398                     provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
 399                     test(provider, true);
 400                     System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
 401                     break;
 402                 case NOPERMISSIONS:
 403                     System.out.println("\n*** With Security Manager, without permissions\n");
 404                     System.out.println(TestLoggerFinder.conf.get());
 405                     setSecurityManager();
 406                     try {
 407                         provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
 408                         throw new RuntimeException("Expected exception not raised");
 409                     } catch (AccessControlException x) {
 410                         if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
 411                             throw new RuntimeException("Unexpected permission check", x);
 412                         }
 413                         final boolean control = allowControl.get().get();
 414                         try {
 415                             allowControl.get().set(true);
 416                             provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
 417                         } finally {
 418                             allowControl.get().set(control);
 419                         }
 420                     }
 421                     test(provider, false);
 422                     System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
 423                     break;
 424                 case WITHPERMISSIONS:
 425                     System.out.println("\n*** With Security Manager, with control permission\n");
 426                     System.out.println(TestLoggerFinder.conf.get());
 427                     setSecurityManager();
 428                     final boolean control = allowControl.get().get();
 429                     try {
 430                         allowControl.get().set(true);
 431                         provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
 432                         test(provider, true);
 433                     } finally {
 434                         allowControl.get().set(control);
 435                     }
 436                     break;
 437                 default:
 438                     throw new RuntimeException("Unknown test case: " + testCase);
 439             }
 440         });
 441         System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
 442     }
 443 
 444     public static void test(LoggerFinder provider, boolean hasRequiredPermissions) {
 445 
 446         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 447         final Map<Logger, String> loggerDescMap = new HashMap<>();
 448 
 449         System.Logger sysLogger = accessSystemLogger.getLogger("foo");
 450         loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
 451         System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle);
 452         loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
 453         System.Logger appLogger = System.getLogger("bar");
 454         loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
 455         System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle);
 456         loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
 457 
 458         testLogger(provider, loggerDescMap, "foo", null, sysLogger);
 459         testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger);
 460         testLogger(provider, loggerDescMap, "foo", null, appLogger);
 461         testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger);
 462     }
 463 
 464     public static class Foo {
 465 
 466     }
 467 
 468     static void verbose(String msg) {
 469        if (VERBOSE) {
 470            System.out.println(msg);
 471        }
 472     }
 473 
 474     // Calls the 8 methods defined on Logger and verify the
 475     // parameters received by the underlying TestProvider.LoggerImpl
 476     // logger.
 477     private static void testLogger(LoggerFinder provider,
 478             Map<Logger, String> loggerDescMap,
 479             String name,
 480             ResourceBundle loggerBundle,
 481             Logger logger) {
 482 
 483         System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
 484         AtomicLong sequencer = TestLoggerFinder.sequencer;
 485 
 486         Foo foo = new Foo();
 487         String fooMsg = foo.toString();
 488         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 489             for (Level messageLevel : Level.values()) {
 490                 ErrorStream.errorStream.drain();
 491                 String desc = "logger.log(messageLevel, foo): loggerLevel="
 492                         + loggerLevel+", messageLevel="+messageLevel;
 493                 sequencer.incrementAndGet();
 494                 logger.log(messageLevel, foo);
 495                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 496                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 497                         throw new RuntimeException("unexpected event in queue for "
 498                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 499                     }
 500                 } else {
 501                     String logged = ErrorStream.errorStream.drain();
 502                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 503                         || !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
 504                         throw new RuntimeException("mismatch for " + desc
 505                                 + "\n\texpected:" + "\n<<<<\n"
 506                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 507                                 + messageLevel.getName() + " " + fooMsg
 508                                 + "\n>>>>"
 509                                 + "\n\t  actual:"
 510                                 + "\n<<<<\n" + logged + ">>>>\n");
 511                     } else {
 512                         verbose("Got expected results for "
 513                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 514                     }
 515                 }
 516             }
 517         }
 518 
 519         String msg = "blah";
 520         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 521             for (Level messageLevel : Level.values()) {
 522                 String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
 523                         + loggerLevel+", messageLevel="+messageLevel;
 524                 sequencer.incrementAndGet();
 525                 logger.log(messageLevel, msg);
 526                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 527                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 528                         throw new RuntimeException("unexpected event in queue for "
 529                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 530                     }
 531                 } else {
 532                     String logged = ErrorStream.errorStream.drain();
 533                     String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
 534                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 535                         || !logged.contains(messageLevel.getName() + ": " + msgText)) {
 536                         throw new RuntimeException("mismatch for " + desc
 537                                 + "\n\texpected:" + "\n<<<<\n"
 538                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 539                                 + messageLevel.getName() + " " + msgText
 540                                 + "\n>>>>"
 541                                 + "\n\t  actual:"
 542                                 + "\n<<<<\n" + logged + ">>>>\n");
 543                     } else {
 544                         verbose("Got expected results for "
 545                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 546                     }
 547                 }
 548             }
 549         }
 550 
 551         Supplier<String> fooSupplier = new Supplier<String>() {
 552             @Override
 553             public String get() {
 554                 return this.toString();
 555             }
 556         };
 557 
 558         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 559             for (Level messageLevel : Level.values()) {
 560                 String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
 561                         + loggerLevel+", messageLevel="+messageLevel;
 562                 sequencer.incrementAndGet();
 563                 logger.log(messageLevel, fooSupplier);
 564                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 565                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 566                         throw new RuntimeException("unexpected event in queue for "
 567                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 568                     }
 569                 } else {
 570                     String logged = ErrorStream.errorStream.drain();
 571                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 572                         || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
 573                         throw new RuntimeException("mismatch for " + desc
 574                                 + "\n\texpected:" + "\n<<<<\n"
 575                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 576                                 + messageLevel.getName() + " " + fooSupplier.get()
 577                                 + "\n>>>>"
 578                                 + "\n\t  actual:"
 579                                 + "\n<<<<\n" + logged + ">>>>\n");
 580                     } else {
 581                         verbose("Got expected results for "
 582                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 583                     }
 584                 }
 585             }
 586         }
 587 
 588 
 589         String format = "two params [{1} {2}]";
 590         Object arg1 = foo;
 591         Object arg2 = msg;
 592         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 593             for (Level messageLevel : Level.values()) {
 594                 String desc = "logger.log(messageLevel, format, params...): loggerLevel="
 595                         + loggerLevel+", messageLevel="+messageLevel;
 596                 sequencer.incrementAndGet();
 597                 logger.log(messageLevel, format, foo, msg);
 598                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 599                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 600                         throw new RuntimeException("unexpected event in queue for "
 601                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 602                     }
 603                 } else {
 604                     String logged = ErrorStream.errorStream.drain();
 605                     String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
 606                     String text = java.text.MessageFormat.format(msgFormat, foo, msg);
 607                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 608                         || !logged.contains(messageLevel.getName() + ": " + text)) {
 609                         throw new RuntimeException("mismatch for " + desc
 610                                 + "\n\texpected:" + "\n<<<<\n"
 611                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 612                                 + messageLevel.getName() + " " + text
 613                                 + "\n>>>>"
 614                                 + "\n\t  actual:"
 615                                 + "\n<<<<\n" + logged + ">>>>\n");
 616                     } else {
 617                         verbose("Got expected results for "
 618                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 619                     }
 620                 }
 621             }
 622         }
 623 
 624         Throwable thrown = new Exception("OK: log me!");
 625         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 626             for (Level messageLevel : Level.values()) {
 627                 String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
 628                         + loggerLevel+", messageLevel="+messageLevel;
 629                 sequencer.incrementAndGet();
 630                 logger.log(messageLevel, msg, thrown);
 631                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 632                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 633                         throw new RuntimeException("unexpected event in queue for "
 634                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 635                     }
 636                 } else {
 637                     String logged = ErrorStream.errorStream.drain();
 638                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 639                     thrown.printStackTrace(new PrintStream(baos));
 640                     String text = baos.toString();
 641                     String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
 642                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 643                         || !logged.contains(messageLevel.getName() + ": " + msgText)
 644                         || !logged.contains(text)) {
 645                         throw new RuntimeException("mismatch for " + desc
 646                                 + "\n\texpected:" + "\n<<<<\n"
 647                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 648                                 + messageLevel.getName() + " " + msgText +"\n"
 649                                 + text
 650                                 + ">>>>"
 651                                 + "\n\t  actual:"
 652                                 + "\n<<<<\n" + logged + ">>>>\n");
 653                     } else {
 654                         verbose("Got expected results for "
 655                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 656                     }
 657                 }
 658             }
 659         }
 660 
 661 
 662         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 663             for (Level messageLevel : Level.values()) {
 664                 String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
 665                         + loggerLevel+", messageLevel="+messageLevel;
 666                 sequencer.incrementAndGet();
 667                 logger.log(messageLevel, fooSupplier, thrown);
 668                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 669                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 670                         throw new RuntimeException("unexpected event in queue for "
 671                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 672                     }
 673                 } else {
 674                     String logged = ErrorStream.errorStream.drain();
 675                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 676                     thrown.printStackTrace(new PrintStream(baos));
 677                     String text = baos.toString();
 678                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 679                         || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
 680                         || !logged.contains(text)) {
 681                         throw new RuntimeException("mismatch for " + desc
 682                                 + "\n\texpected:" + "\n<<<<\n"
 683                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 684                                 + messageLevel.getName() + " " + fooSupplier.get() +"\n"
 685                                 + text
 686                                 + ">>>>"
 687                                 + "\n\t  actual:"
 688                                 + "\n<<<<\n" + logged + ">>>>\n");
 689                     } else {
 690                         verbose("Got expected results for "
 691                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 692                     }
 693                 }
 694             }
 695         }
 696 
 697         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 698         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 699             for (Level messageLevel : Level.values()) {
 700                 String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
 701                         + loggerLevel+", messageLevel="+messageLevel;
 702                 sequencer.incrementAndGet();
 703                 logger.log(messageLevel, bundle, format, foo, msg);
 704                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 705                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 706                         throw new RuntimeException("unexpected event in queue for "
 707                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 708                     }
 709                 } else {
 710                     String logged = ErrorStream.errorStream.drain();
 711                     String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
 712                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 713                         || !logged.contains(messageLevel.getName() + ": " + text)) {
 714                         throw new RuntimeException("mismatch for " + desc
 715                                 + "\n\texpected:" + "\n<<<<\n"
 716                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 717                                 + messageLevel.getName() + " " + text
 718                                 + "\n>>>>"
 719                                 + "\n\t  actual:"
 720                                 + "\n<<<<\n" + logged + ">>>>\n");
 721                     } else {
 722                         verbose("Got expected results for "
 723                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 724                     }
 725                 }
 726             }
 727         }
 728 
 729         for (Level loggerLevel : EnumSet.of(Level.INFO)) {
 730             for (Level messageLevel : Level.values()) {
 731                 String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
 732                         + loggerLevel+", messageLevel="+messageLevel;
 733                 sequencer.incrementAndGet();
 734                 logger.log(messageLevel, bundle, msg, thrown);
 735                 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
 736                     if (!ErrorStream.errorStream.peek().isEmpty()) {
 737                         throw new RuntimeException("unexpected event in queue for "
 738                                 + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
 739                     }
 740                 } else {
 741                     String logged = ErrorStream.errorStream.drain();
 742                     String textMsg = bundle.getString(msg);
 743                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
 744                     thrown.printStackTrace(new PrintStream(baos));
 745                     String text = baos.toString();
 746                     if (!logged.contains("LoggerFinderLoaderTest testLogger")
 747                         || !logged.contains(messageLevel.getName() + ": " + textMsg)
 748                         || !logged.contains(text)) {
 749                         throw new RuntimeException("mismatch for " + desc
 750                                 + "\n\texpected:" + "\n<<<<\n"
 751                                 + "[date] LoggerFinderLoaderTest testLogger\n"
 752                                 + messageLevel.getName() + " " + textMsg +"\n"
 753                                 + text
 754                                 + ">>>>"
 755                                 + "\n\t  actual:"
 756                                 + "\n<<<<\n" + logged + ">>>>\n");
 757                     } else {
 758                         verbose("Got expected results for "
 759                                 + desc + "\n<<<<\n" + logged + ">>>>\n");
 760                     }
 761                 }
 762             }
 763         }
 764 
 765     }
 766 
 767     final static class PermissionsBuilder {
 768         final Permissions perms;
 769         public PermissionsBuilder() {
 770             this(new Permissions());
 771         }
 772         public PermissionsBuilder(Permissions perms) {
 773             this.perms = perms;
 774         }
 775         public PermissionsBuilder add(Permission p) {
 776             perms.add(p);
 777             return this;
 778         }
 779         public PermissionsBuilder addAll(PermissionCollection col) {
 780             if (col != null) {
 781                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 782                     perms.add(e.nextElement());
 783                 }
 784             }
 785             return this;
 786         }
 787         public Permissions toPermissions() {
 788             final PermissionsBuilder builder = new PermissionsBuilder();
 789             builder.addAll(perms);
 790             return builder.perms;
 791         }
 792     }
 793 
 794     public static class SimplePolicy extends Policy {
 795         final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
 796         final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
 797 
 798         final Permissions permissions;
 799         final ThreadLocal<AtomicBoolean> allowControl;
 800         final ThreadLocal<AtomicBoolean> allowAccess;
 801         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
 802             this.allowControl = allowControl;
 803             this.allowAccess = allowAccess;
 804             permissions = new Permissions();
 805             permissions.add(new RuntimePermission("setIO"));
 806         }
 807 
 808         Permissions getPermissions() {
 809             if (allowControl.get().get() || allowAccess.get().get()) {
 810                 PermissionsBuilder builder =  new PermissionsBuilder()
 811                         .addAll(permissions);
 812                 if (allowControl.get().get()) {
 813                     builder.add(CONTROL);
 814                 }
 815                 if (allowAccess.get().get()) {
 816                     builder.add(ACCESS);
 817                 }
 818                 return builder.toPermissions();
 819             }
 820             return permissions;
 821         }
 822 
 823         @Override
 824         public boolean implies(ProtectionDomain domain, Permission permission) {
 825             return getPermissions().implies(permission) ||
 826                    DEFAULT_POLICY.implies(domain, permission);
 827         }
 828 
 829         @Override
 830         public PermissionCollection getPermissions(CodeSource codesource) {
 831             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
 832         }
 833 
 834         @Override
 835         public PermissionCollection getPermissions(ProtectionDomain domain) {
 836             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
 837         }
 838     }
 839 }