1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.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 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("BaseLoggerBridgeTest$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 
 213 
 214             @Override
 215             public boolean equals(Object obj) {
 216                 return obj instanceof LogEvent
 217                         && Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj)));
 218             }
 219 
 220             @Override
 221             public int hashCode() {
 222                 return Objects.hash(toArray(true));
 223             }
 224 
 225             public LogEvent cloneWith(long sequenceNumber)
 226                     throws CloneNotSupportedException {
 227                 LogEvent cloned = (LogEvent)super.clone();
 228                 cloned.sequenceNumber = sequenceNumber;
 229                 return cloned;
 230             }
 231 
 232             public static LogEvent of(boolean isLoggable, String name,
 233                     Level level, ResourceBundle bundle,
 234                     String key, Throwable thrown) {
 235                 LogEvent evt = new LogEvent();
 236                 evt.isLoggable = isLoggable;
 237                 evt.loggerName = name;
 238                 evt.level = level;
 239                 evt.args = null;
 240                 evt.bundle = bundle;
 241                 evt.thrown = thrown;
 242                 evt.supplier = null;
 243                 evt.msg = key;
 244                 return evt;
 245             }
 246 
 247             public static LogEvent of(boolean isLoggable, String name,
 248                     Level level, Throwable thrown, Supplier<String> supplier) {
 249                 LogEvent evt = new LogEvent();
 250                 evt.isLoggable = isLoggable;
 251                 evt.loggerName = name;
 252                 evt.level = level;
 253                 evt.args = null;
 254                 evt.bundle = null;
 255                 evt.thrown = thrown;
 256                 evt.supplier = supplier;
 257                 evt.msg = null;
 258                 return evt;
 259             }
 260 
 261             public static LogEvent of(boolean isLoggable, String name,
 262                     Level level, ResourceBundle bundle,
 263                     String key, Object... params) {
 264                 LogEvent evt = new LogEvent();
 265                 evt.isLoggable = isLoggable;
 266                 evt.loggerName = name;
 267                 evt.level = level;
 268                 evt.args = params;
 269                 evt.bundle = bundle;
 270                 evt.thrown = null;
 271                 evt.supplier = null;
 272                 evt.msg = key;
 273                 return evt;
 274             }
 275 
 276             public static LogEvent of(long sequenceNumber,
 277                     boolean isLoggable, String name,
 278                     Level level, ResourceBundle bundle,
 279                     String key, Supplier<String> supplier,
 280                     Throwable thrown, Object... params) {
 281                 LogEvent evt = new LogEvent(sequenceNumber);
 282                 evt.loggerName = name;
 283                 evt.level = level;
 284                 evt.args = params;
 285                 evt.bundle = bundle;
 286                 evt.thrown = thrown;
 287                 evt.supplier = supplier;
 288                 evt.msg = key;
 289                 evt.isLoggable = isLoggable;
 290                 return evt;
 291             }
 292 
 293             public static LogEvent ofp(boolean callSupplier, LogEvent evt) {
 294                 evt.callSupplier = callSupplier;
 295                 return evt;
 296             }
 297         }
 298 
 299         public class LoggerImpl implements Logger {
 300             private final String name;
 301             private Level level = Level.INFO;
 302 
 303             public LoggerImpl(String name) {
 304                 this.name = name;
 305             }
 306 
 307             @Override
 308             public String getName() {
 309                 return name;
 310             }
 311 
 312             @Override
 313             public boolean isLoggable(Level level) {
 314                 return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
 315             }
 316 
 317             @Override
 318             public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
 319                 log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
 320             }
 321 
 322             @Override
 323             public void log(Level level, ResourceBundle bundle, String format, Object... params) {
 324                 log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
 325             }
 326 
 327             void log(LogEvent event) {
 328                 eventQueue.add(event);
 329             }
 330 
 331             @Override
 332             public void log(Level level, Supplier<String> msgSupplier) {
 333                 log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
 334             }
 335 
 336             @Override
 337             public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
 338                 log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
 339             }
 340 
 341 
 342 
 343         }
 344 
 345         public Logger getLogger(String name, Class<?> caller);
 346         public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
 347     }
 348 
 349     public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
 350         static final RuntimePermission LOGGERFINDER_PERMISSION =
 351                 new RuntimePermission("loggerFinder");
 352         @Override
 353         public Logger getLogger(String name, Class<?> caller) {
 354             SecurityManager sm = System.getSecurityManager();
 355             if (sm != null) {
 356                 sm.checkPermission(LOGGERFINDER_PERMISSION);
 357             }
 358             PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
 359             ClassLoader callerLoader = AccessController.doPrivileged(pa);
 360             if (callerLoader == null) {
 361                 return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
 362             } else {
 363                 return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
 364             }
 365         }
 366     }
 367 
 368     static PlatformLogger.Bridge convert(Logger logger) {
 369         boolean old = allowAll.get().get();
 370         allowAccess.get().set(true);
 371         try {
 372             return PlatformLogger.Bridge.convert(logger);
 373         } finally {
 374             allowAccess.get().set(old);
 375         }
 376     }
 377 
 378     static Logger getLogger(String name, Class<?> caller) {
 379         boolean old = allowAll.get().get();
 380         allowAccess.get().set(true);
 381         try {
 382             return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
 383         } finally {
 384             allowAccess.get().set(old);
 385         }
 386     }
 387 
 388     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
 389 
 390     static void setSecurityManager() {
 391         if (System.getSecurityManager() == null) {
 392             // Ugly test hack: preload the resources needed by String.format
 393             //   We need to do that before setting the security manager
 394             //   because our implementation of CustomSystemClassLoader
 395             //   doesn't have the required permission.
 396             System.out.println(String.format("debug: %s", "Setting security manager"));
 397             Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
 398             System.setSecurityManager(new SecurityManager());
 399         }
 400     }
 401 
 402     public static void main(String[] args) {
 403         if (args.length == 0)
 404             args = new String[] {
 405                 "NOSECURITY",
 406                 "NOPERMISSIONS",
 407                 "WITHPERMISSIONS"
 408             };
 409 
 410 
 411         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
 412             TestLoggerFinder provider;
 413             switch (testCase) {
 414                 case NOSECURITY:
 415                     System.out.println("\n*** Without Security Manager\n");
 416                     provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 417                     test(provider, true);
 418                     System.out.println("Tetscase count: " + sequencer.get());
 419                     break;
 420                 case NOPERMISSIONS:
 421                     System.out.println("\n*** With Security Manager, without permissions\n");
 422                     setSecurityManager();
 423                     try {
 424                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 425                         throw new RuntimeException("Expected exception not raised");
 426                     } catch (AccessControlException x) {
 427                         if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
 428                             throw new RuntimeException("Unexpected permission check", x);
 429                         }
 430                         final boolean control = allowControl.get().get();
 431                         try {
 432                             allowControl.get().set(true);
 433                             provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 434                         } finally {
 435                             allowControl.get().set(control);
 436                         }
 437                     }
 438                     test(provider, false);
 439                     System.out.println("Tetscase count: " + sequencer.get());
 440                     break;
 441                 case WITHPERMISSIONS:
 442                     System.out.println("\n*** With Security Manager, with control permission\n");
 443                     setSecurityManager();
 444                     final boolean control = allowControl.get().get();
 445                     try {
 446                         allowControl.get().set(true);
 447                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
 448                         test(provider, true);
 449                     } finally {
 450                         allowControl.get().set(control);
 451                     }
 452                     break;
 453                 default:
 454                     throw new RuntimeException("Unknown test case: " + testCase);
 455             }
 456         });
 457         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 458     }
 459 
 460     public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
 461 
 462         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 463         final Map<Object, String> loggerDescMap = new HashMap<>();
 464 
 465 
 466         TestLoggerFinder.LoggerImpl appSink = null;
 467         try {
 468             appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class));
 469             if (!hasRequiredPermissions) {
 470                 throw new RuntimeException("Managed to obtain a system logger without permission");
 471             }
 472         } catch (AccessControlException acx) {
 473             if (hasRequiredPermissions) {
 474                 throw new RuntimeException("Unexpected security exception: ", acx);
 475             }
 476             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 477                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 478             }
 479             System.out.println("Got expected exception for logger: " + acx);
 480             boolean old = allowControl.get().get();
 481             allowControl.get().set(true);
 482             try {
 483                 appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class));
 484             } finally {
 485                 allowControl.get().set(old);
 486             }
 487         }
 488 
 489 
 490         TestLoggerFinder.LoggerImpl sysSink = null;
 491         try {
 492             sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
 493             if (!hasRequiredPermissions) {
 494                 throw new RuntimeException("Managed to obtain a system logger without permission");
 495             }
 496         } catch (AccessControlException acx) {
 497             if (hasRequiredPermissions) {
 498                 throw new RuntimeException("Unexpected security exception: ", acx);
 499             }
 500             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 501                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 502             }
 503             System.out.println("Got expected exception for system logger: " + acx);
 504         }
 505         if (hasRequiredPermissions && appSink == sysSink) {
 506             throw new RuntimeException("identical loggers");
 507         }
 508 
 509         if (provider.system.contains(appSink)) {
 510             throw new RuntimeException("app logger in system map");
 511         }
 512         if (!provider.user.contains(appSink)) {
 513             throw new RuntimeException("app logger not in appplication map");
 514         }
 515         if (hasRequiredPermissions && provider.user.contains(sysSink)) {
 516             throw new RuntimeException("sys logger in appplication map");
 517         }
 518         if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
 519             throw new RuntimeException("sys logger not in system map");
 520         }
 521 
 522         Logger appLogger1 = System.getLogger("foo");
 523         loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
 524         PlatformLogger.Bridge bridge = convert(appLogger1);
 525         loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
 526         testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
 527 
 528         Logger sysLogger1 = null;
 529         try {
 530             sysLogger1 = getLogger("foo", Thread.class);
 531             loggerDescMap.put(sysLogger1,
 532                     "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class)");
 533 
 534             if (!hasRequiredPermissions) {
 535                 // check that the provider would have thrown an exception
 536                 provider.getLogger("foo", Thread.class);
 537                 throw new RuntimeException("Managed to obtain a system logger without permission");
 538             }
 539         } catch (AccessControlException acx) {
 540             if (hasRequiredPermissions) {
 541                 throw new RuntimeException("Unexpected security exception: ", acx);
 542             }
 543             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 544                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 545             }
 546             System.out.println("Got expected exception for system logger: " + acx);
 547         }
 548 
 549         if (hasRequiredPermissions) {
 550             // if we don't have permissions sysSink will be null.
 551             testLogger(provider, loggerDescMap, "foo", null,
 552                 PlatformLogger.Bridge.convert(sysLogger1), sysSink);
 553         }
 554 
 555         Logger appLogger2 =
 556                 System.getLogger("foo", loggerBundle);
 557         loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
 558 
 559         if (appLogger2 == appLogger1) {
 560             throw new RuntimeException("identical loggers");
 561         }
 562 
 563         if (provider.system.contains(appLogger2)) {
 564             throw new RuntimeException("localized app logger in system map");
 565         }
 566         if (provider.user.contains(appLogger2)) {
 567             throw new RuntimeException("localized app logger  in appplication map");
 568         }
 569 
 570         testLogger(provider, loggerDescMap, "foo", loggerBundle,
 571                 PlatformLogger.Bridge.convert(appLogger2), appSink);
 572 
 573         Logger sysLogger2 = null;
 574         try {
 575             sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
 576             loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)");
 577             if (!hasRequiredPermissions) {
 578                 throw new RuntimeException("Managed to obtain a system logger without permission");
 579             }
 580         } catch (AccessControlException acx) {
 581             if (hasRequiredPermissions) {
 582                 throw new RuntimeException("Unexpected security exception: ", acx);
 583             }
 584             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
 585                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
 586             }
 587             System.out.println("Got expected exception for localized system logger: " + acx);
 588         }
 589         if (hasRequiredPermissions && appLogger2 == sysLogger2) {
 590             throw new RuntimeException("identical loggers");
 591         }
 592         if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
 593             throw new RuntimeException("identical loggers");
 594         }
 595         if (hasRequiredPermissions && provider.user.contains(sysLogger2)) {
 596             throw new RuntimeException("localized sys logger in appplication map");
 597         }
 598         if (hasRequiredPermissions && provider.system.contains(sysLogger2)) {
 599             throw new RuntimeException("localized sys logger not in system map");
 600         }
 601 
 602         if (hasRequiredPermissions) {
 603             // if we don't have permissions sysSink will be null.
 604             testLogger(provider, loggerDescMap, "foo", loggerBundle,
 605                 PlatformLogger.Bridge.convert(sysLogger2), sysSink);
 606         }
 607 
 608     }
 609 
 610     public static class Foo {
 611 
 612     }
 613 
 614     static void verbose(String msg) {
 615        if (VERBOSE) {
 616            System.out.println(msg);
 617        }
 618     }
 619 
 620     static void checkLogEvent(TestLoggerFinder provider, String desc,
 621             TestLoggerFinder.LogEvent expected) {
 622         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 623         if (!Objects.equals(expected, actual)) {
 624             throw new RuntimeException("mismatch for " + desc
 625                     + "\n\texpected=" + expected
 626                     + "\n\t  actual=" + actual);
 627         } else {
 628             verbose("Got expected results for "
 629                     + desc + "\n\t" + expected);
 630         }
 631     }
 632 
 633     static void checkLogEvent(TestLoggerFinder provider, String desc,
 634             TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
 635         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
 636         if (actual == null && !expectNotNull) return;
 637         if (actual != null && !expectNotNull) {
 638             throw new RuntimeException("Unexpected log event found for " + desc
 639                 + "\n\tgot: " + actual);
 640         }
 641         if (!expected.equals(actual)) {
 642             throw new RuntimeException("mismatch for " + desc
 643                     + "\n\texpected=" + expected
 644                     + "\n\t  actual=" + actual);
 645         } else {
 646             verbose("Got expected results for "
 647                     + desc + "\n\t" + expected);
 648         }
 649     }
 650 
 651         static Supplier<String> logpMessage(ResourceBundle bundle,
 652                 String className, String methodName, Supplier<String> msg) {
 653             if (BEST_EFFORT_FOR_LOGP && bundle == null
 654                     && (className != null || methodName != null)) {
 655                 final String cName = className == null ? "" :  className;
 656                 final String mName = methodName == null ? "" : methodName;
 657                 return () -> String.format("[%s %s] %s", cName, mName, msg.get());
 658             } else {
 659                 return msg;
 660             }
 661         }
 662 
 663         static String logpMessage(ResourceBundle bundle,
 664                 String className, String methodName, String msg) {
 665             if (BEST_EFFORT_FOR_LOGP && bundle == null
 666                     && (className != null || methodName != null)) {
 667                 final String cName = className == null ? "" :  className;
 668                 final String mName = methodName == null ? "" : methodName;
 669                 return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
 670             } else {
 671                 return msg;
 672             }
 673         }
 674 
 675     // Calls the methods defined on LogProducer and verify the
 676     // parameters received by the underlying TestLoggerFinder.LoggerImpl
 677     // logger.
 678     private static void testLogger(TestLoggerFinder provider,
 679             Map<Object, String> loggerDescMap,
 680             String name,
 681             ResourceBundle loggerBundle,
 682             PlatformLogger.Bridge logger,
 683             TestLoggerFinder.LoggerImpl sink) {
 684 
 685         if (loggerDescMap.get(logger) == null) {
 686             throw new RuntimeException("Test bug: Missing description");
 687         }
 688         System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
 689 
 690         Foo foo = new Foo();
 691         String fooMsg = foo.toString();
 692         System.out.println("\tlogger.log(messageLevel, fooMsg)");
 693         System.out.println("\tlogger.<level>(fooMsg)");
 694         for (Level loggerLevel : Level.values()) {
 695             sink.level = loggerLevel;
 696             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 697                 String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
 698                         + loggerLevel+", messageLevel="+messageLevel;
 699                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 700                 TestLoggerFinder.LogEvent expected =
 701                         TestLoggerFinder.LogEvent.of(
 702                             sequencer.get(),
 703                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 704                             name, expectedMessageLevel, loggerBundle,
 705                             fooMsg, null, (Throwable)null, (Object[])null);
 706                 logger.log(messageLevel, fooMsg);
 707                 checkLogEvent(provider, desc, expected);
 708             }
 709         }
 710 
 711         Supplier<String> supplier = new Supplier<String>() {
 712             @Override
 713             public String get() {
 714                 return this.toString();
 715             }
 716         };
 717         System.out.println("\tlogger.log(messageLevel, supplier)");
 718         System.out.println("\tlogger.<level>(supplier)");
 719         for (Level loggerLevel : Level.values()) {
 720             sink.level = loggerLevel;
 721             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 722                 String desc = "logger.log(messageLevel, supplier): loggerLevel="
 723                         + loggerLevel+", messageLevel="+messageLevel;
 724                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 725                 TestLoggerFinder.LogEvent expected =
 726                         TestLoggerFinder.LogEvent.of(
 727                             sequencer.get(),
 728                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 729                             name, expectedMessageLevel, (ResourceBundle) null,
 730                             null, supplier, (Throwable)null, (Object[])null);
 731                 logger.log(messageLevel, supplier);
 732                 checkLogEvent(provider, desc, expected);
 733             }
 734         }
 735 
 736         String format = "two params [{1} {2}]";
 737         Object arg1 = foo;
 738         Object arg2 = fooMsg;
 739         System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
 740         for (Level loggerLevel : Level.values()) {
 741             sink.level = loggerLevel;
 742             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 743                 String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
 744                         + loggerLevel+", messageLevel="+messageLevel;
 745                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 746                 TestLoggerFinder.LogEvent expected =
 747                         TestLoggerFinder.LogEvent.of(
 748                             sequencer.get(),
 749                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 750                             name, expectedMessageLevel, loggerBundle,
 751                             format, null, (Throwable)null, arg1, arg2);
 752                 logger.log(messageLevel, format, arg1, arg2);
 753                 checkLogEvent(provider, desc, expected);
 754             }
 755         }
 756 
 757         Throwable thrown = new Exception("OK: log me!");
 758         System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
 759         for (Level loggerLevel : Level.values()) {
 760             sink.level = loggerLevel;
 761             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 762                 String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
 763                         + loggerLevel+", messageLevel="+messageLevel;
 764                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 765                 TestLoggerFinder.LogEvent expected =
 766                         TestLoggerFinder.LogEvent.of(
 767                             sequencer.get(),
 768                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 769                             name, expectedMessageLevel, loggerBundle,
 770                             fooMsg, null, thrown, (Object[])null);
 771                 logger.log(messageLevel, fooMsg, thrown);
 772                 checkLogEvent(provider, desc, expected);
 773             }
 774         }
 775 
 776         System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
 777         for (Level loggerLevel : Level.values()) {
 778             sink.level = loggerLevel;
 779             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 780                 String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
 781                         + loggerLevel+", messageLevel="+messageLevel;
 782                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 783                 TestLoggerFinder.LogEvent expected =
 784                         TestLoggerFinder.LogEvent.of(
 785                             sequencer.get(),
 786                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 787                             name, expectedMessageLevel, (ResourceBundle)null,
 788                             null, supplier, thrown, (Object[])null);
 789                 logger.log(messageLevel, thrown, supplier);
 790                 checkLogEvent(provider, desc, expected);
 791             }
 792         }
 793 
 794         String sourceClass = "blah.Blah";
 795         String sourceMethod = "blih";
 796         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
 797         for (Level loggerLevel : Level.values()) {
 798             sink.level = loggerLevel;
 799             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 800                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
 801                         + loggerLevel+", messageLevel="+messageLevel;
 802                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 803                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 804                 TestLoggerFinder.LogEvent expected =
 805                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
 806                         TestLoggerFinder.LogEvent.of(
 807                             sequencer.get(),
 808                             isLoggable,
 809                             name, expectedMessageLevel, loggerBundle,
 810                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
 811                             null, (Throwable)null, (Object[]) null) : null;
 812                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
 813                 checkLogEvent(provider, desc, expected);
 814             }
 815         }
 816 
 817         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
 818         for (Level loggerLevel : Level.values()) {
 819             sink.level = loggerLevel;
 820             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 821                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
 822                         + loggerLevel+", messageLevel="+messageLevel;
 823                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 824                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 825                 TestLoggerFinder.LogEvent expected = isLoggable ?
 826                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
 827                         TestLoggerFinder.LogEvent.of(
 828                             sequencer.get(),
 829                             isLoggable,
 830                             name, expectedMessageLevel, null, null,
 831                             logpMessage(null, sourceClass, sourceMethod, supplier),
 832                             (Throwable)null, (Object[]) null)) : null;
 833                 logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
 834                 checkLogEvent(provider, desc, expected);
 835             }
 836         }
 837 
 838         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
 839         for (Level loggerLevel : Level.values()) {
 840             sink.level = loggerLevel;
 841             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 842                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
 843                         + loggerLevel+", messageLevel="+messageLevel;
 844                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 845                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 846                 TestLoggerFinder.LogEvent expected =
 847                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
 848                         TestLoggerFinder.LogEvent.of(
 849                             sequencer.get(),
 850                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 851                             name, expectedMessageLevel, loggerBundle,
 852                             logpMessage(loggerBundle, sourceClass, sourceMethod, format),
 853                             null, (Throwable)null, arg1, arg2) : null;
 854                 logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
 855                 checkLogEvent(provider, desc, expected);
 856             }
 857         }
 858 
 859         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
 860         for (Level loggerLevel : Level.values()) {
 861             sink.level = loggerLevel;
 862             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 863                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
 864                         + loggerLevel+", messageLevel="+messageLevel;
 865                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 866                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 867                 TestLoggerFinder.LogEvent expected =
 868                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
 869                         TestLoggerFinder.LogEvent.of(
 870                             sequencer.get(),
 871                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 872                             name, expectedMessageLevel, loggerBundle,
 873                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
 874                             null, thrown, (Object[])null) : null;
 875                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
 876                 checkLogEvent(provider, desc, expected);
 877             }
 878         }
 879 
 880         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
 881         for (Level loggerLevel : Level.values()) {
 882             sink.level = loggerLevel;
 883             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 884                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
 885                         + loggerLevel+", messageLevel="+messageLevel;
 886                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 887                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
 888                 TestLoggerFinder.LogEvent expected = isLoggable ?
 889                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
 890                         TestLoggerFinder.LogEvent.of(
 891                             sequencer.get(),
 892                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 893                             name, expectedMessageLevel, null, null,
 894                             logpMessage(null, sourceClass, sourceMethod, supplier),
 895                             thrown, (Object[])null)) : null;
 896                 logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
 897                 checkLogEvent(provider, desc, expected);
 898             }
 899         }
 900 
 901         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
 902         System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
 903         for (Level loggerLevel : Level.values()) {
 904             sink.level = loggerLevel;
 905             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 906                 String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
 907                         + loggerLevel+", messageLevel="+messageLevel;
 908                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 909                 TestLoggerFinder.LogEvent expected =
 910                         TestLoggerFinder.LogEvent.of(
 911                             sequencer.get(),
 912                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 913                             name, expectedMessageLevel, bundle,
 914                             format, null, (Throwable)null, arg1, arg2);
 915                 logger.logrb(messageLevel, bundle, format, arg1, arg2);
 916                 checkLogEvent(provider, desc, expected);
 917             }
 918         }
 919 
 920         System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
 921         for (Level loggerLevel : Level.values()) {
 922             sink.level = loggerLevel;
 923             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 924                 String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
 925                         + loggerLevel+", messageLevel="+messageLevel;
 926                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 927                 TestLoggerFinder.LogEvent expected =
 928                         TestLoggerFinder.LogEvent.of(
 929                             sequencer.get(),
 930                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 931                             name, expectedMessageLevel, bundle,
 932                             fooMsg, null, thrown, (Object[])null);
 933                 logger.logrb(messageLevel, bundle, fooMsg, thrown);
 934                 checkLogEvent(provider, desc, expected);
 935             }
 936         }
 937 
 938         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
 939         for (Level loggerLevel : Level.values()) {
 940             sink.level = loggerLevel;
 941             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 942                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
 943                         + loggerLevel+", messageLevel="+messageLevel;
 944                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 945                 TestLoggerFinder.LogEvent expected =
 946                         TestLoggerFinder.LogEvent.of(
 947                             sequencer.get(),
 948                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 949                             name, expectedMessageLevel, bundle,
 950                             format, null, (Throwable)null, arg1, arg2);
 951                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
 952                 checkLogEvent(provider, desc, expected);
 953             }
 954         }
 955 
 956         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
 957         for (Level loggerLevel : Level.values()) {
 958             sink.level = loggerLevel;
 959             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
 960                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
 961                         + loggerLevel+", messageLevel="+messageLevel;
 962                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
 963                 TestLoggerFinder.LogEvent expected =
 964                         TestLoggerFinder.LogEvent.of(
 965                             sequencer.get(),
 966                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
 967                             name, expectedMessageLevel, bundle,
 968                             fooMsg, null, thrown, (Object[])null);
 969                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
 970                 checkLogEvent(provider, desc, expected);
 971             }
 972         }
 973     }
 974 
 975     final static class PermissionsBuilder {
 976         final Permissions perms;
 977         public PermissionsBuilder() {
 978             this(new Permissions());
 979         }
 980         public PermissionsBuilder(Permissions perms) {
 981             this.perms = perms;
 982         }
 983         public PermissionsBuilder add(Permission p) {
 984             perms.add(p);
 985             return this;
 986         }
 987         public PermissionsBuilder addAll(PermissionCollection col) {
 988             if (col != null) {
 989                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 990                     perms.add(e.nextElement());
 991                 }
 992             }
 993             return this;
 994         }
 995         public Permissions toPermissions() {
 996             final PermissionsBuilder builder = new PermissionsBuilder();
 997             builder.addAll(perms);
 998             return builder.perms;
 999         }
1000     }
1001 
1002     public static class SimplePolicy extends Policy {
1003         final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
1004         final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
1005         final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
1006 
1007         final Permissions permissions;
1008         final Permissions allPermissions;
1009         final ThreadLocal<AtomicBoolean> allowControl;
1010         final ThreadLocal<AtomicBoolean> allowAccess;
1011         final ThreadLocal<AtomicBoolean> allowAll;
1012         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
1013                 ThreadLocal<AtomicBoolean> allowAccess,
1014                 ThreadLocal<AtomicBoolean> allowAll) {
1015             this.allowControl = allowControl;
1016             this.allowAccess = allowAccess;
1017             this.allowAll = allowAll;
1018             permissions = new Permissions();
1019             allPermissions = new PermissionsBuilder()
1020                     .add(new java.security.AllPermission())
1021                     .toPermissions();
1022         }
1023 
1024         Permissions getPermissions() {
1025             if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
1026                 PermissionsBuilder builder =  new PermissionsBuilder()
1027                         .addAll(permissions);
1028                 if (allowControl.get().get()) {
1029                     builder.add(CONTROL);
1030                 }
1031                 if (allowAccess.get().get()) {
1032                     builder.add(ACCESS_LOGGER);
1033                     builder.add(ACCESS_LOGGING);
1034                 }
1035                 if (allowAll.get().get()) {
1036                     builder.addAll(allPermissions);
1037                 }
1038                 return builder.toPermissions();
1039             }
1040             return permissions;
1041         }
1042 
1043         @Override
1044         public boolean implies(ProtectionDomain domain, Permission permission) {
1045             return getPermissions().implies(permission);
1046         }
1047 
1048         @Override
1049         public PermissionCollection getPermissions(CodeSource codesource) {
1050             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1051         }
1052 
1053         @Override
1054         public PermissionCollection getPermissions(ProtectionDomain domain) {
1055             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1056         }
1057     }
1058 }