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.security.AccessControlException;
  24 import java.security.AccessController;
  25 import java.security.CodeSource;
  26 import java.security.Permission;
  27 import java.security.PermissionCollection;
  28 import java.security.Permissions;
  29 import java.security.Policy;
  30 import java.security.PrivilegedAction;
  31 import java.security.ProtectionDomain;
  32 import java.util.Arrays;
  33 import java.util.Collections;
  34 import java.util.Enumeration;
  35 import java.util.HashMap;
  36 import java.util.Map;
  37 import java.util.Objects;
  38 import java.util.Queue;
  39 import java.util.ResourceBundle;
  40 import java.util.concurrent.ArrayBlockingQueue;
  41 import java.util.concurrent.ConcurrentHashMap;
  42 import java.util.concurrent.atomic.AtomicBoolean;
  43 import java.util.concurrent.atomic.AtomicLong;
  44 import java.util.function.Supplier;
  45 import sun.util.logging.PlatformLogger;
  46 import java.lang.System.LoggerFinder;
  47 import java.lang.System.Logger;
  48 import java.lang.System.Logger.Level;
  49 import java.util.stream.Stream;
  50 
  51 /**
  52  * @test
  53  * @bug     8140364
  54  * @summary JDK implementation specific unit test for JDK internal artifacts.
  55  *   Tests a naive implementation of System.Logger, and in particular
  56  *   the default mapping provided by PlatformLogger.Bridge.
  57  * @modules java.base/sun.util.logging java.base/jdk.internal.logger
  58  * @build CustomSystemClassLoader BaseLoggerFinder BaseLoggerBridgeTest
  59  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOSECURITY
  60  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOPERMISSIONS
  61  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest WITHPERMISSIONS
  62  * @author danielfuchs
  63  */
  64 public class BaseLoggerBridgeTest {
  65 
  66     static final RuntimePermission LOGGERFINDER_PERMISSION =
  67                 new RuntimePermission("loggerFinder");
  68     final static AtomicLong sequencer = new AtomicLong();
  69     final static boolean VERBOSE = false;
  70     // whether the implementation of Logger try to do a best
  71     // effort for logp... Our base logger finder stub doesn't
  72     // support logp, and thus the logp() implementation comes from
  73     // LoggerWrapper - which does a best effort.
  74     static final boolean BEST_EFFORT_FOR_LOGP = true;
  75     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
  76         @Override
  77         protected AtomicBoolean initialValue() {
  78             return  new AtomicBoolean(false);
  79         }
  80     };
  81     static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
  82         @Override
  83         protected AtomicBoolean initialValue() {
  84             return  new AtomicBoolean(false);
  85         }
  86     };
  87     static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
  88         @Override
  89         protected AtomicBoolean initialValue() {
  90             return  new AtomicBoolean(false);
  91         }
  92     };
  93 
  94     static final Class<?> providerClass;
  95     static {
  96         try {
  97             providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder");
  98         } catch (ClassNotFoundException ex) {
  99             throw new ExceptionInInitializerError(ex);
 100         }
 101     }
 102 
 103     static final sun.util.logging.PlatformLogger.Level[] julLevels = {
 104         sun.util.logging.PlatformLogger.Level.ALL,
 105         sun.util.logging.PlatformLogger.Level.FINEST,
 106         sun.util.logging.PlatformLogger.Level.FINER,
 107         sun.util.logging.PlatformLogger.Level.FINE,
 108         sun.util.logging.PlatformLogger.Level.CONFIG,
 109         sun.util.logging.PlatformLogger.Level.INFO,
 110         sun.util.logging.PlatformLogger.Level.WARNING,
 111         sun.util.logging.PlatformLogger.Level.SEVERE,
 112         sun.util.logging.PlatformLogger.Level.OFF,
 113     };
 114 
 115     static final Level[] mappedLevels = {
 116         Level.ALL,     // ALL
 117         Level.TRACE,   // FINEST
 118         Level.TRACE,   // FINER
 119         Level.DEBUG,   // FINE
 120         Level.DEBUG,   // CONFIG
 121         Level.INFO,    // INFO
 122         Level.WARNING, // WARNING
 123         Level.ERROR,   // SEVERE
 124         Level.OFF,     // OFF
 125     };
 126 
 127     final static Map<sun.util.logging.PlatformLogger.Level, Level> julToSpiMap;
 128     static {
 129         Map<sun.util.logging.PlatformLogger.Level, Level> map = new HashMap<>();
 130         if (mappedLevels.length != julLevels.length) {
 131             throw new ExceptionInInitializerError("Array lengths differ"
 132                 + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
 133                 + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
 134         }
 135         for (int i=0; i<julLevels.length; i++) {
 136             map.put(julLevels[i], mappedLevels[i]);
 137         }
 138         julToSpiMap = Collections.unmodifiableMap(map);
 139     }
 140 
 141     public static class MyBundle extends ResourceBundle {
 142 
 143         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
 144 
 145         @Override
 146         protected Object handleGetObject(String key) {
 147             if (key.contains(" (translated)")) {
 148                 throw new RuntimeException("Unexpected key: " + key);
 149             }
 150             return map.computeIfAbsent(key, k -> k + " (translated)");
 151         }
 152 
 153         @Override
 154         public Enumeration<String> getKeys() {
 155             return Collections.enumeration(map.keySet());
 156         }
 157 
 158     }
 159     public static class MyLoggerBundle extends MyBundle {
 160 
 161     }
 162 
 163     public static interface TestLoggerFinder {
 164         final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
 165         final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
 166         public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
 167 
 168         public static final class LogEvent implements Cloneable {
 169 
 170             public LogEvent() {
 171                 this(sequencer.getAndIncrement());
 172             }
 173 
 174             LogEvent(long sequenceNumber) {
 175                 this.sequenceNumber = sequenceNumber;
 176             }
 177 
 178             boolean callSupplier = false;
 179             long sequenceNumber;
 180             boolean isLoggable;
 181             String loggerName;
 182             Level level;
 183             ResourceBundle bundle;
 184             Throwable thrown;
 185             Object[] args;
 186             Supplier<String> supplier;
 187             String msg;
 188 
 189             Object[] toArray(boolean callSupplier) {
 190                 return new Object[] {
 191                     sequenceNumber,
 192                     isLoggable,
 193                     loggerName,
 194                     level,
 195                     bundle,
 196                     thrown,
 197                     args,
 198                     callSupplier && supplier != null ? supplier.get() : supplier,
 199                     msg,
 200                 };
 201             }
 202 
 203             boolean callSupplier(Object obj) {
 204                 return callSupplier || ((LogEvent)obj).callSupplier;
 205             }
 206 
 207             @Override
 208             public String toString() {
 209                 return Arrays.deepToString(toArray(false));
 210             }
 211 
 212             @Override
 213             public boolean equals(Object obj) {
 214                 return obj instanceof LogEvent
 215                         && Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj)));
 216             }
 217 
 218             @Override
 219             public int hashCode() {
 220                 return Objects.hash(toArray(true));
 221             }
 222 
 223             public LogEvent cloneWith(long sequenceNumber)
 224                     throws CloneNotSupportedException {
 225                 LogEvent cloned = (LogEvent)super.clone();
 226                 cloned.sequenceNumber = sequenceNumber;
 227                 return cloned;
 228             }
 229 
 230             public static LogEvent of(boolean isLoggable, String name,
 231                     Level level, ResourceBundle bundle,
 232                     String key, Throwable thrown) {
 233                 LogEvent evt = new LogEvent();
 234                 evt.isLoggable = isLoggable;
 235                 evt.loggerName = name;
 236                 evt.level = level;
 237                 evt.args = null;
 238                 evt.bundle = bundle;
 239                 evt.thrown = thrown;
 240                 evt.supplier = null;
 241                 evt.msg = key;
 242                 return evt;
 243             }
 244 
 245             public static LogEvent of(boolean isLoggable, String name,
 246                     Level level, Throwable thrown, Supplier<String> supplier) {
 247                 LogEvent evt = new LogEvent();
 248                 evt.isLoggable = isLoggable;
 249                 evt.loggerName = name;
 250                 evt.level = level;
 251                 evt.args = null;
 252                 evt.bundle = null;
 253                 evt.thrown = thrown;
 254                 evt.supplier = supplier;
 255                 evt.msg = null;
 256                 return evt;
 257             }
 258 
 259             public static LogEvent of(boolean isLoggable, String name,
 260                     Level level, ResourceBundle bundle,
 261                     String key, Object... params) {
 262                 LogEvent evt = new LogEvent();
 263                 evt.isLoggable = isLoggable;
 264                 evt.loggerName = name;
 265                 evt.level = level;
 266                 evt.args = params;
 267                 evt.bundle = bundle;
 268                 evt.thrown = null;
 269                 evt.supplier = null;
 270                 evt.msg = key;
 271                 return evt;
 272             }
 273 
 274             public static LogEvent of(long sequenceNumber,
 275                     boolean isLoggable, String name,
 276                     Level level, ResourceBundle bundle,
 277                     String key, Supplier<String> supplier,
 278                     Throwable thrown, Object... params) {
 279                 LogEvent evt = new LogEvent(sequenceNumber);
 280                 evt.loggerName = name;
 281                 evt.level = level;
 282                 evt.args = params;
 283                 evt.bundle = bundle;
 284                 evt.thrown = thrown;
 285                 evt.supplier = supplier;
 286                 evt.msg = key;
 287                 evt.isLoggable = isLoggable;
 288                 return evt;
 289             }
 290 
 291             public static LogEvent ofp(boolean callSupplier, LogEvent evt) {
 292                 evt.callSupplier = callSupplier;
 293                 return evt;
 294             }
 295         }
 296 
 297         public class LoggerImpl implements Logger {
 298             private final String name;
 299             private Level level = Level.INFO;
 300 
 301             public LoggerImpl(String name) {
 302                 this.name = name;
 303             }
 304 
 305             @Override
 306             public String getName() {
 307                 return name;
 308             }
 309 
 310             @Override
 311             public boolean isLoggable(Level level) {
 312                 return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
 313             }
 314 
 315             @Override
 316             public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
 317                 log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
 318             }
 319 
 320             @Override
 321             public void log(Level level, ResourceBundle bundle, String format, Object... params) {
 322                 log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
 323             }
 324 
 325             void log(LogEvent event) {
 326                 eventQueue.add(event);
 327             }
 328 
 329             @Override
 330             public void log(Level level, Supplier<String> msgSupplier) {
 331                 log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
 332             }
 333 
 334             @Override
 335             public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
 336                 log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
 337             }
 338 
 339         }
 340 
 341         public Logger getLogger(String name, Module caller);
 342         public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
 343     }
 344 
 345     static PlatformLogger.Bridge convert(Logger logger) {
 346         boolean old = allowAll.get().get();
 347         allowAccess.get().set(true);
 348         try {
 349             return PlatformLogger.Bridge.convert(logger);
 350         } finally {
 351             allowAccess.get().set(old);
 352         }
 353     }
 354 
 355     static Logger getLogger(String name, Module caller) {
 356         boolean old = allowAll.get().get();
 357         allowAccess.get().set(true);
 358         try {
 359             return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
 360         } finally {
 361             allowAccess.get().set(old);
 362         }
 363     }
 364 
 365     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
 366 
 367     static void setSecurityManager() {
 368         if (System.getSecurityManager() == null) {
 369             // Ugly test hack: preload the resources needed by String.format
 370             //   We need to do that before setting the security manager
 371             //   because our implementation of CustomSystemClassLoader
 372             //   doesn't have the required permission.
 373             System.out.println(String.format("debug: %s", "Setting security manager"));
 374             Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
 375             System.setSecurityManager(new SecurityManager());
 376         }
 377     }
 378 
 379     public static void main(String[] args) {
 380         if (args.length == 0)
 381             args = new String[] {
 382                 "NOSECURITY",
 383                 "NOPERMISSIONS",
 384                 "WITHPERMISSIONS"
 385             };
 386 
 387 
 388         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 389             TestLoggerFinder provider;
 390             switch (testCase) {
 391                 case NOSECURITY:
 392                     System.out.println("\n*** Without Security Manager\n");
 393                     provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 394                     test(provider, true);
 395                     System.out.println("Tetscase count: " + sequencer.get());
 396                     break;
 397                 case NOPERMISSIONS:
 398                     System.out.println("\n*** With Security Manager, without permissions\n");
 399                     setSecurityManager();
 400                     try {
 401                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 402                         throw new RuntimeException("Expected exception not raised");
 403                     } catch (AccessControlException x) {
 404                         if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
 405                             throw new RuntimeException("Unexpected permission check", x);
 406                         }
 407                         final boolean control = allowControl.get().get();
 408                         try {
 409                             allowControl.get().set(true);
 410                             provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 411                         } finally {
 412                             allowControl.get().set(control);
 413                         }
 414                     }
 415                     test(provider, false);
 416                     System.out.println("Tetscase count: " + sequencer.get());
 417                     break;
 418                 case WITHPERMISSIONS:
 419                     System.out.println("\n*** With Security Manager, with control permission\n");
 420                     setSecurityManager();
 421                     final boolean control = allowControl.get().get();
 422                     try {
 423                         allowControl.get().set(true);
 424                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 425                         test(provider, true);
 426                     } finally {
 427                         allowControl.get().set(control);
 428                     }
 429                     break;
 430                 default:
 431                     throw new RuntimeException("Unknown test case: " + testCase);
 432             }
 433         });
 434         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 435     }
 436 
 437     public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
 438 
 439         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 440         final Map<Object, String> loggerDescMap = new HashMap<>();
 441 
 442 
 443         TestLoggerFinder.LoggerImpl appSink = null;
 444         try {
 445             appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
 446             if (!hasRequiredPermissions) {
 447                 throw new RuntimeException("Managed to obtain a system logger without permission");
 448             }
 449         } catch (AccessControlException acx) {
 450             if (hasRequiredPermissions) {
 451                 throw new RuntimeException("Unexpected security exception: ", acx);
 452             }
 453             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 454                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 455             }
 456             System.out.println("Got expected exception for logger: " + acx);
 457             boolean old = allowControl.get().get();
 458             allowControl.get().set(true);
 459             try {
 460                 appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
 461             } finally {
 462                 allowControl.get().set(old);
 463             }
 464         }
 465 
 466 
 467         TestLoggerFinder.LoggerImpl sysSink = null;
 468         try {
 469             sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));
 470             if (!hasRequiredPermissions) {
 471                 throw new RuntimeException("Managed to obtain a system logger without permission");
 472             }
 473         } catch (AccessControlException acx) {
 474             if (hasRequiredPermissions) {
 475                 throw new RuntimeException("Unexpected security exception: ", acx);
 476             }
 477             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 478                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 479             }
 480             System.out.println("Got expected exception for system logger: " + acx);
 481         }
 482         if (hasRequiredPermissions && appSink == sysSink) {
 483             throw new RuntimeException("identical loggers");
 484         }
 485 
 486         if (provider.system.contains(appSink)) {
 487             throw new RuntimeException("app logger in system map");
 488         }
 489         if (!provider.user.contains(appSink)) {
 490             throw new RuntimeException("app logger not in appplication map");
 491         }
 492         if (hasRequiredPermissions && provider.user.contains(sysSink)) {
 493             throw new RuntimeException("sys logger in appplication map");
 494         }
 495         if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
 496             throw new RuntimeException("sys logger not in system map");
 497         }
 498 
 499         Logger appLogger1 = System.getLogger("foo");
 500         loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
 501         PlatformLogger.Bridge bridge = convert(appLogger1);
 502         loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
 503         testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
 504 
 505         Logger sysLogger1 = null;
 506         try {
 507             sysLogger1 = getLogger("foo", Thread.class.getModule());
 508             loggerDescMap.put(sysLogger1,
 509                     "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class.getModule())");
 510 
 511             if (!hasRequiredPermissions) {
 512                 // check that the provider would have thrown an exception
 513                 provider.getLogger("foo", Thread.class.getModule());
 514                 throw new RuntimeException("Managed to obtain a system logger without permission");
 515             }
 516         } catch (AccessControlException acx) {
 517             if (hasRequiredPermissions) {
 518                 throw new RuntimeException("Unexpected security exception: ", acx);
 519             }
 520             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 521                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 522             }
 523             System.out.println("Got expected exception for system logger: " + acx);
 524         }
 525 
 526         if (hasRequiredPermissions) {
 527             // if we don't have permissions sysSink will be null.
 528             testLogger(provider, loggerDescMap, "foo", null,
 529                 PlatformLogger.Bridge.convert(sysLogger1), sysSink);
 530         }
 531 
 532         Logger appLogger2 =
 533                 System.getLogger("foo", loggerBundle);
 534         loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
 535 
 536         if (appLogger2 == appLogger1) {
 537             throw new RuntimeException("identical loggers");
 538         }
 539 
 540         if (provider.system.contains(appLogger2)) {
 541             throw new RuntimeException("localized app logger in system map");
 542         }
 543         if (provider.user.contains(appLogger2)) {
 544             throw new RuntimeException("localized app logger  in appplication map");
 545         }
 546 
 547         testLogger(provider, loggerDescMap, "foo", loggerBundle,
 548                 PlatformLogger.Bridge.convert(appLogger2), appSink);
 549 
 550         Logger sysLogger2 = null;
 551         try {
 552             sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
 553             loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
 554             if (!hasRequiredPermissions) {
 555                 throw new RuntimeException("Managed to obtain a system logger without permission");
 556             }
 557         } catch (AccessControlException acx) {
 558             if (hasRequiredPermissions) {
 559                 throw new RuntimeException("Unexpected security exception: ", acx);
 560             }
 561             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 562                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 563             }
 564             System.out.println("Got expected exception for localized system logger: " + acx);
 565         }
 566         if (hasRequiredPermissions && appLogger2 == sysLogger2) {
 567             throw new RuntimeException("identical loggers");
 568         }
 569         if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
 570             throw new RuntimeException("identical loggers");
 571         }
 572         if (hasRequiredPermissions && provider.user.contains(sysLogger2)) {
 573             throw new RuntimeException("localized sys logger in appplication map");
 574         }
 575         if (hasRequiredPermissions && provider.system.contains(sysLogger2)) {
 576             throw new RuntimeException("localized sys logger not in system map");
 577         }
 578 
 579         if (hasRequiredPermissions) {
 580             // if we don't have permissions sysSink will be null.
 581             testLogger(provider, loggerDescMap, "foo", loggerBundle,
 582                 PlatformLogger.Bridge.convert(sysLogger2), sysSink);
 583         }
 584 
 585     }
 586 
 587     public static class Foo {
 588 
 589     }
 590 
 591     static void verbose(String msg) {
 592        if (VERBOSE) {
 593            System.out.println(msg);
 594        }
 595     }
 596 
 597     static void checkLogEvent(TestLoggerFinder provider, String desc,
 598             TestLoggerFinder.LogEvent expected) {
 599         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 600         if (!Objects.equals(expected, actual)) {
 601             throw new RuntimeException("mismatch for " + desc
 602                     + "\n\texpected=" + expected
 603                     + "\n\t  actual=" + actual);
 604         } else {
 605             verbose("Got expected results for "
 606                     + desc + "\n\t" + expected);
 607         }
 608     }
 609 
 610     static void checkLogEvent(TestLoggerFinder provider, String desc,
 611             TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
 612         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 613         if (actual == null && !expectNotNull) return;
 614         if (actual != null && !expectNotNull) {
 615             throw new RuntimeException("Unexpected log event found for " + desc
 616                 + "\n\tgot: " + actual);
 617         }
 618         if (!expected.equals(actual)) {
 619             throw new RuntimeException("mismatch for " + desc
 620                     + "\n\texpected=" + expected
 621                     + "\n\t  actual=" + actual);
 622         } else {
 623             verbose("Got expected results for "
 624                     + desc + "\n\t" + expected);
 625         }
 626     }
 627 
 628         static Supplier<String> logpMessage(ResourceBundle bundle,
 629                 String className, String methodName, Supplier<String> msg) {
 630             if (BEST_EFFORT_FOR_LOGP && bundle == null
 631                     && (className != null || methodName != null)) {
 632                 final String cName = className == null ? "" :  className;
 633                 final String mName = methodName == null ? "" : methodName;
 634                 return () -> String.format("[%s %s] %s", cName, mName, msg.get());
 635             } else {
 636                 return msg;
 637             }
 638         }
 639 
 640         static String logpMessage(ResourceBundle bundle,
 641                 String className, String methodName, String msg) {
 642             if (BEST_EFFORT_FOR_LOGP && bundle == null
 643                     && (className != null || methodName != null)) {
 644                 final String cName = className == null ? "" :  className;
 645                 final String mName = methodName == null ? "" : methodName;
 646                 return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
 647             } else {
 648                 return msg;
 649             }
 650         }
 651 
 652     // Calls the methods defined on LogProducer and verify the
 653     // parameters received by the underlying TestLoggerFinder.LoggerImpl
 654     // logger.
 655     private static void testLogger(TestLoggerFinder provider,
 656             Map<Object, String> loggerDescMap,
 657             String name,
 658             ResourceBundle loggerBundle,
 659             PlatformLogger.Bridge logger,
 660             TestLoggerFinder.LoggerImpl sink) {
 661 
 662         if (loggerDescMap.get(logger) == null) {
 663             throw new RuntimeException("Test bug: Missing description");
 664         }
 665         System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
 666 
 667         Foo foo = new Foo();
 668         String fooMsg = foo.toString();
 669         System.out.println("\tlogger.log(messageLevel, fooMsg)");
 670         System.out.println("\tlogger.<level>(fooMsg)");
 671         for (Level loggerLevel : Level.values()) {
 672             sink.level = loggerLevel;
 673             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 674                 String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
 675                         + loggerLevel+", messageLevel="+messageLevel;
 676                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 677                 TestLoggerFinder.LogEvent expected =
 678                         TestLoggerFinder.LogEvent.of(
 679                             sequencer.get(),
 680                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 681                             name, expectedMessageLevel, loggerBundle,
 682                             fooMsg, null, (Throwable)null, (Object[])null);
 683                 logger.log(messageLevel, fooMsg);
 684                 checkLogEvent(provider, desc, expected);
 685             }
 686         }
 687 
 688         Supplier<String> supplier = new Supplier<String>() {
 689             @Override
 690             public String get() {
 691                 return this.toString();
 692             }
 693         };
 694         System.out.println("\tlogger.log(messageLevel, supplier)");
 695         System.out.println("\tlogger.<level>(supplier)");
 696         for (Level loggerLevel : Level.values()) {
 697             sink.level = loggerLevel;
 698             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 699                 String desc = "logger.log(messageLevel, supplier): loggerLevel="
 700                         + loggerLevel+", messageLevel="+messageLevel;
 701                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 702                 TestLoggerFinder.LogEvent expected =
 703                         TestLoggerFinder.LogEvent.of(
 704                             sequencer.get(),
 705                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 706                             name, expectedMessageLevel, (ResourceBundle) null,
 707                             null, supplier, (Throwable)null, (Object[])null);
 708                 logger.log(messageLevel, supplier);
 709                 checkLogEvent(provider, desc, expected);
 710             }
 711         }
 712 
 713         String format = "two params [{1} {2}]";
 714         Object arg1 = foo;
 715         Object arg2 = fooMsg;
 716         System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
 717         for (Level loggerLevel : Level.values()) {
 718             sink.level = loggerLevel;
 719             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 720                 String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
 721                         + loggerLevel+", messageLevel="+messageLevel;
 722                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 723                 TestLoggerFinder.LogEvent expected =
 724                         TestLoggerFinder.LogEvent.of(
 725                             sequencer.get(),
 726                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 727                             name, expectedMessageLevel, loggerBundle,
 728                             format, null, (Throwable)null, arg1, arg2);
 729                 logger.log(messageLevel, format, arg1, arg2);
 730                 checkLogEvent(provider, desc, expected);
 731             }
 732         }
 733 
 734         Throwable thrown = new Exception("OK: log me!");
 735         System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
 736         for (Level loggerLevel : Level.values()) {
 737             sink.level = loggerLevel;
 738             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 739                 String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
 740                         + loggerLevel+", messageLevel="+messageLevel;
 741                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 742                 TestLoggerFinder.LogEvent expected =
 743                         TestLoggerFinder.LogEvent.of(
 744                             sequencer.get(),
 745                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 746                             name, expectedMessageLevel, loggerBundle,
 747                             fooMsg, null, thrown, (Object[])null);
 748                 logger.log(messageLevel, fooMsg, thrown);
 749                 checkLogEvent(provider, desc, expected);
 750             }
 751         }
 752 
 753         System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
 754         for (Level loggerLevel : Level.values()) {
 755             sink.level = loggerLevel;
 756             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 757                 String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
 758                         + loggerLevel+", messageLevel="+messageLevel;
 759                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 760                 TestLoggerFinder.LogEvent expected =
 761                         TestLoggerFinder.LogEvent.of(
 762                             sequencer.get(),
 763                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 764                             name, expectedMessageLevel, (ResourceBundle)null,
 765                             null, supplier, thrown, (Object[])null);
 766                 logger.log(messageLevel, thrown, supplier);
 767                 checkLogEvent(provider, desc, expected);
 768             }
 769         }
 770 
 771         String sourceClass = "blah.Blah";
 772         String sourceMethod = "blih";
 773         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
 774         for (Level loggerLevel : Level.values()) {
 775             sink.level = loggerLevel;
 776             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 777                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
 778                         + loggerLevel+", messageLevel="+messageLevel;
 779                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 780                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 781                 TestLoggerFinder.LogEvent expected =
 782                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
 783                         TestLoggerFinder.LogEvent.of(
 784                             sequencer.get(),
 785                             isLoggable,
 786                             name, expectedMessageLevel, loggerBundle,
 787                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
 788                             null, (Throwable)null, (Object[]) null) : null;
 789                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
 790                 checkLogEvent(provider, desc, expected);
 791             }
 792         }
 793 
 794         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
 795         for (Level loggerLevel : Level.values()) {
 796             sink.level = loggerLevel;
 797             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 798                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
 799                         + loggerLevel+", messageLevel="+messageLevel;
 800                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 801                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 802                 TestLoggerFinder.LogEvent expected = isLoggable ?
 803                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
 804                         TestLoggerFinder.LogEvent.of(
 805                             sequencer.get(),
 806                             isLoggable,
 807                             name, expectedMessageLevel, null, null,
 808                             logpMessage(null, sourceClass, sourceMethod, supplier),
 809                             (Throwable)null, (Object[]) null)) : null;
 810                 logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
 811                 checkLogEvent(provider, desc, expected);
 812             }
 813         }
 814 
 815         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
 816         for (Level loggerLevel : Level.values()) {
 817             sink.level = loggerLevel;
 818             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 819                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
 820                         + loggerLevel+", messageLevel="+messageLevel;
 821                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 822                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 823                 TestLoggerFinder.LogEvent expected =
 824                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
 825                         TestLoggerFinder.LogEvent.of(
 826                             sequencer.get(),
 827                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 828                             name, expectedMessageLevel, loggerBundle,
 829                             logpMessage(loggerBundle, sourceClass, sourceMethod, format),
 830                             null, (Throwable)null, arg1, arg2) : null;
 831                 logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
 832                 checkLogEvent(provider, desc, expected);
 833             }
 834         }
 835 
 836         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
 837         for (Level loggerLevel : Level.values()) {
 838             sink.level = loggerLevel;
 839             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 840                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
 841                         + loggerLevel+", messageLevel="+messageLevel;
 842                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 843                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 844                 TestLoggerFinder.LogEvent expected =
 845                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
 846                         TestLoggerFinder.LogEvent.of(
 847                             sequencer.get(),
 848                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 849                             name, expectedMessageLevel, loggerBundle,
 850                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
 851                             null, thrown, (Object[])null) : null;
 852                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
 853                 checkLogEvent(provider, desc, expected);
 854             }
 855         }
 856 
 857         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
 858         for (Level loggerLevel : Level.values()) {
 859             sink.level = loggerLevel;
 860             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 861                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
 862                         + loggerLevel+", messageLevel="+messageLevel;
 863                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 864                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 865                 TestLoggerFinder.LogEvent expected = isLoggable ?
 866                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
 867                         TestLoggerFinder.LogEvent.of(
 868                             sequencer.get(),
 869                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 870                             name, expectedMessageLevel, null, null,
 871                             logpMessage(null, sourceClass, sourceMethod, supplier),
 872                             thrown, (Object[])null)) : null;
 873                 logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
 874                 checkLogEvent(provider, desc, expected);
 875             }
 876         }
 877 
 878         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 879         System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
 880         for (Level loggerLevel : Level.values()) {
 881             sink.level = loggerLevel;
 882             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 883                 String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
 884                         + loggerLevel+", messageLevel="+messageLevel;
 885                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 886                 TestLoggerFinder.LogEvent expected =
 887                         TestLoggerFinder.LogEvent.of(
 888                             sequencer.get(),
 889                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 890                             name, expectedMessageLevel, bundle,
 891                             format, null, (Throwable)null, arg1, arg2);
 892                 logger.logrb(messageLevel, bundle, format, arg1, arg2);
 893                 checkLogEvent(provider, desc, expected);
 894             }
 895         }
 896 
 897         System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
 898         for (Level loggerLevel : Level.values()) {
 899             sink.level = loggerLevel;
 900             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 901                 String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
 902                         + loggerLevel+", messageLevel="+messageLevel;
 903                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 904                 TestLoggerFinder.LogEvent expected =
 905                         TestLoggerFinder.LogEvent.of(
 906                             sequencer.get(),
 907                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 908                             name, expectedMessageLevel, bundle,
 909                             fooMsg, null, thrown, (Object[])null);
 910                 logger.logrb(messageLevel, bundle, fooMsg, thrown);
 911                 checkLogEvent(provider, desc, expected);
 912             }
 913         }
 914 
 915         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
 916         for (Level loggerLevel : Level.values()) {
 917             sink.level = loggerLevel;
 918             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 919                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
 920                         + loggerLevel+", messageLevel="+messageLevel;
 921                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 922                 TestLoggerFinder.LogEvent expected =
 923                         TestLoggerFinder.LogEvent.of(
 924                             sequencer.get(),
 925                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 926                             name, expectedMessageLevel, bundle,
 927                             format, null, (Throwable)null, arg1, arg2);
 928                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
 929                 checkLogEvent(provider, desc, expected);
 930             }
 931         }
 932 
 933         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
 934         for (Level loggerLevel : Level.values()) {
 935             sink.level = loggerLevel;
 936             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 937                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
 938                         + loggerLevel+", messageLevel="+messageLevel;
 939                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 940                 TestLoggerFinder.LogEvent expected =
 941                         TestLoggerFinder.LogEvent.of(
 942                             sequencer.get(),
 943                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 944                             name, expectedMessageLevel, bundle,
 945                             fooMsg, null, thrown, (Object[])null);
 946                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
 947                 checkLogEvent(provider, desc, expected);
 948             }
 949         }
 950     }
 951 
 952     final static class PermissionsBuilder {
 953         final Permissions perms;
 954         public PermissionsBuilder() {
 955             this(new Permissions());
 956         }
 957         public PermissionsBuilder(Permissions perms) {
 958             this.perms = perms;
 959         }
 960         public PermissionsBuilder add(Permission p) {
 961             perms.add(p);
 962             return this;
 963         }
 964         public PermissionsBuilder addAll(PermissionCollection col) {
 965             if (col != null) {
 966                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 967                     perms.add(e.nextElement());
 968                 }
 969             }
 970             return this;
 971         }
 972         public Permissions toPermissions() {
 973             final PermissionsBuilder builder = new PermissionsBuilder();
 974             builder.addAll(perms);
 975             return builder.perms;
 976         }
 977     }
 978 
 979     public static class SimplePolicy extends Policy {
 980         final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
 981         final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
 982         final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
 983 
 984         final Permissions permissions;
 985         final Permissions allPermissions;
 986         final ThreadLocal<AtomicBoolean> allowControl;
 987         final ThreadLocal<AtomicBoolean> allowAccess;
 988         final ThreadLocal<AtomicBoolean> allowAll;
 989         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
 990                 ThreadLocal<AtomicBoolean> allowAccess,
 991                 ThreadLocal<AtomicBoolean> allowAll) {
 992             this.allowControl = allowControl;
 993             this.allowAccess = allowAccess;
 994             this.allowAll = allowAll;
 995             permissions = new Permissions();
 996             allPermissions = new PermissionsBuilder()
 997                     .add(new java.security.AllPermission())
 998                     .toPermissions();
 999         }
1000 
1001         Permissions getPermissions() {
1002             if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
1003                 PermissionsBuilder builder =  new PermissionsBuilder()
1004                         .addAll(permissions);
1005                 if (allowControl.get().get()) {
1006                     builder.add(CONTROL);
1007                 }
1008                 if (allowAccess.get().get()) {
1009                     builder.add(ACCESS_LOGGER);
1010                     builder.add(ACCESS_LOGGING);
1011                 }
1012                 if (allowAll.get().get()) {
1013                     builder.addAll(allPermissions);
1014                 }
1015                 return builder.toPermissions();
1016             }
1017             return permissions;
1018         }
1019 
1020         @Override
1021         public boolean implies(ProtectionDomain domain, Permission permission) {
1022             return getPermissions().implies(permission);
1023         }
1024 
1025         @Override
1026         public PermissionCollection getPermissions(CodeSource codesource) {
1027             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1028         }
1029 
1030         @Override
1031         public PermissionCollection getPermissions(ProtectionDomain domain) {
1032             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1033         }
1034     }
1035 }