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.CodeSource;
  24 import java.security.Permission;
  25 import java.security.PermissionCollection;
  26 import java.security.Permissions;
  27 import java.security.Policy;
  28 import java.security.ProtectionDomain;
  29 import java.util.Arrays;
  30 import java.util.Collections;
  31 import java.util.Enumeration;
  32 import java.util.HashMap;
  33 import java.util.Map;
  34 import java.util.Objects;
  35 import java.util.Queue;
  36 import java.util.ResourceBundle;
  37 import java.util.concurrent.ArrayBlockingQueue;
  38 import java.util.concurrent.ConcurrentHashMap;
  39 import java.util.concurrent.atomic.AtomicBoolean;
  40 import java.util.concurrent.atomic.AtomicLong;
  41 import java.util.logging.Handler;
  42 import java.util.logging.LogManager;
  43 import java.util.logging.LogRecord;
  44 import java.lang.System.LoggerFinder;
  45 import sun.util.logging.PlatformLogger;
  46 import sun.util.logging.internal.LoggingProviderImpl;
  47 import java.lang.reflect.Module;
  48 
  49 /**
  50  * @test
  51  * @bug     8140364
  52  * @summary Tests all PlatformLogger methods with the default LoggerFinder JUL backend.
  53  * @modules java.base/sun.util.logging java.logging/sun.util.logging.internal
  54  * @run  main/othervm DefaultPlatformLoggerTest
  55  * @author danielfuchs
  56  */
  57 public class DefaultPlatformLoggerTest {
  58 
  59     final static AtomicLong sequencer = new AtomicLong();
  60     final static boolean VERBOSE = false;
  61     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
  62         @Override
  63         protected AtomicBoolean initialValue() {
  64             return  new AtomicBoolean(false);
  65         }
  66     };
  67     static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
  68         @Override
  69         protected AtomicBoolean initialValue() {
  70             return  new AtomicBoolean(false);
  71         }
  72     };
  73 
  74     public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
  75 
  76     public static final class LogEvent implements Cloneable {
  77 
  78         public LogEvent() {
  79             this(sequencer.getAndIncrement());
  80         }
  81 
  82         LogEvent(long sequenceNumber) {
  83             this.sequenceNumber = sequenceNumber;
  84         }
  85 
  86         long sequenceNumber;
  87         boolean isLoggable;
  88         String loggerName;
  89         java.util.logging.Level level;
  90         ResourceBundle bundle;
  91         Throwable thrown;
  92         Object[] args;
  93         String msg;
  94         String className;
  95         String methodName;
  96 
  97         Object[] toArray() {
  98             return new Object[] {
  99                 sequenceNumber,
 100                 isLoggable,
 101                 loggerName,
 102                 level,
 103                 bundle,
 104                 thrown,
 105                 args,
 106                 msg,
 107                 className,
 108                 methodName,
 109             };
 110         }
 111 
 112         @Override
 113         public String toString() {
 114             return Arrays.deepToString(toArray());
 115         }
 116 
 117         @Override
 118         public boolean equals(Object obj) {
 119             return obj instanceof LogEvent
 120                     && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
 121         }
 122 
 123         @Override
 124         public int hashCode() {
 125             return Objects.hash(toArray());
 126         }
 127 
 128         public LogEvent cloneWith(long sequenceNumber)
 129                 throws CloneNotSupportedException {
 130             LogEvent cloned = (LogEvent)super.clone();
 131             cloned.sequenceNumber = sequenceNumber;
 132             return cloned;
 133         }
 134 
 135         public static LogEvent of(long sequenceNumber,
 136                 boolean isLoggable, String name,
 137                 java.util.logging.Level level, ResourceBundle bundle,
 138                 String key, Throwable thrown, Object... params) {
 139             return LogEvent.of(sequenceNumber, isLoggable, name,
 140                     DefaultPlatformLoggerTest.class.getName(),
 141                     "testLogger", level, bundle, key,
 142                     thrown, params);
 143         }
 144         public static LogEvent of(long sequenceNumber,
 145                 boolean isLoggable, String name,
 146                 String className, String methodName,
 147                 java.util.logging.Level level, ResourceBundle bundle,
 148                 String key, Throwable thrown, Object... params) {
 149             LogEvent evt = new LogEvent(sequenceNumber);
 150             evt.loggerName = name;
 151             evt.level = level;
 152             evt.args = params;
 153             evt.bundle = bundle;
 154             evt.thrown = thrown;
 155             evt.msg = key;
 156             evt.isLoggable = isLoggable;
 157             evt.className = className;
 158             evt.methodName = methodName;
 159             return evt;
 160         }
 161 
 162     }
 163 
 164     static final java.util.logging.Level[] julLevels = {
 165         java.util.logging.Level.ALL,
 166         new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
 167         java.util.logging.Level.FINEST,
 168         new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
 169         java.util.logging.Level.FINER,
 170         new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
 171         java.util.logging.Level.FINE,
 172         new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
 173         java.util.logging.Level.CONFIG,
 174         new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
 175         java.util.logging.Level.INFO,
 176         new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
 177         java.util.logging.Level.WARNING,
 178         new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
 179         java.util.logging.Level.SEVERE,
 180         new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
 181         java.util.logging.Level.OFF,
 182     };
 183 
 184     static final java.util.logging.Level[] julPlatformLevels = {
 185         java.util.logging.Level.FINEST,
 186         java.util.logging.Level.FINER,
 187         java.util.logging.Level.FINE,
 188         java.util.logging.Level.CONFIG,
 189         java.util.logging.Level.INFO,
 190         java.util.logging.Level.WARNING,
 191         java.util.logging.Level.SEVERE,
 192     };
 193 
 194 
 195     public static class MyBundle extends ResourceBundle {
 196 
 197         final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
 198 
 199         @Override
 200         protected Object handleGetObject(String key) {
 201             if (key.contains(" (translated)")) {
 202                 throw new RuntimeException("Unexpected key: " + key);
 203             }
 204             return map.computeIfAbsent(key, k -> k + " (translated)");
 205         }
 206 
 207         @Override
 208         public Enumeration<String> getKeys() {
 209             return Collections.enumeration(map.keySet());
 210         }
 211 
 212     }
 213 
 214     public static class MyHandler extends Handler {
 215 
 216         @Override
 217         public java.util.logging.Level getLevel() {
 218             return java.util.logging.Level.ALL;
 219         }
 220 
 221         @Override
 222         public void publish(LogRecord record) {
 223             eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
 224                     true, record.getLoggerName(),
 225                     record.getSourceClassName(),
 226                     record.getSourceMethodName(),
 227                     record.getLevel(),
 228                     record.getResourceBundle(), record.getMessage(),
 229                     record.getThrown(), record.getParameters()));
 230         }
 231         @Override
 232         public void flush() {
 233         }
 234         @Override
 235         public void close() throws SecurityException {
 236         }
 237 
 238     }
 239 
 240     public static class MyLoggerBundle extends MyBundle {
 241 
 242     }
 243 
 244     public static void main(String[] args) throws Exception {
 245         LoggerFinder provider = LoggerFinder.getLoggerFinder();
 246         java.util.logging.Logger appSink = LoggingProviderImpl.getLogManagerAccess()
 247                 .demandLoggerFor(LogManager.getLogManager(), "foo",
 248                         DefaultPlatformLoggerTest.class.getModule());
 249         java.util.logging.Logger sysSink = LoggingProviderImpl.getLogManagerAccess()
 250                 .demandLoggerFor(LogManager.getLogManager(),"foo", Thread.class.getModule());
 251         appSink.addHandler(new MyHandler());
 252         sysSink.addHandler(new MyHandler());
 253         appSink.setUseParentHandlers(VERBOSE);
 254         sysSink.setUseParentHandlers(VERBOSE);
 255 
 256         System.out.println("\n*** Without Security Manager\n");
 257         test(provider, true, appSink, sysSink);
 258         System.out.println("Tetscase count: " + sequencer.get());
 259 
 260         Policy.setPolicy(new SimplePolicy(allowAll, allowControl));
 261         System.setSecurityManager(new SecurityManager());
 262 
 263         System.out.println("\n*** With Security Manager, without permissions\n");
 264         test(provider, false, appSink, sysSink);
 265         System.out.println("Tetscase count: " + sequencer.get());
 266 
 267         System.out.println("\n*** With Security Manager, with control permission\n");
 268         allowControl.get().set(true);
 269         test(provider, true, appSink, sysSink);
 270 
 271         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
 272     }
 273 
 274     public static void test(LoggerFinder provider, boolean hasRequiredPermissions,
 275             java.util.logging.Logger appSink, java.util.logging.Logger sysSink) throws Exception {
 276 
 277         // No way to giva a resource bundle to a platform logger.
 278         // ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
 279         final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
 280 
 281         PlatformLogger platform = PlatformLogger.getLogger("foo");
 282         loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
 283 
 284         testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
 285     }
 286 
 287     public static class Foo {
 288 
 289     }
 290 
 291     static void verbose(String msg) {
 292        if (VERBOSE) {
 293            System.out.println(msg);
 294        }
 295     }
 296 
 297     static void checkLogEvent(LoggerFinder provider, String desc,
 298             LogEvent expected) {
 299         LogEvent actual =  eventQueue.poll();
 300         if (!expected.equals(actual)) {
 301             throw new RuntimeException("mismatch for " + desc
 302                     + "\n\texpected=" + expected
 303                     + "\n\t  actual=" + actual);
 304         } else {
 305             verbose("Got expected results for "
 306                     + desc + "\n\t" + expected);
 307         }
 308     }
 309 
 310     static void checkLogEvent(LoggerFinder provider, String desc,
 311             LogEvent expected, boolean expectNotNull) {
 312         LogEvent actual =  eventQueue.poll();
 313         if (actual == null && !expectNotNull) return;
 314         if (actual != null && !expectNotNull) {
 315             throw new RuntimeException("Unexpected log event found for " + desc
 316                 + "\n\tgot: " + actual);
 317         }
 318         if (!expected.equals(actual)) {
 319             throw new RuntimeException("mismatch for " + desc
 320                     + "\n\texpected=" + expected
 321                     + "\n\t  actual=" + actual);
 322         } else {
 323             verbose("Got expected results for "
 324                     + desc + "\n\t" + expected);
 325         }
 326     }
 327 
 328     static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
 329         boolean before = allowAll.get().get();
 330         try {
 331             allowAll.get().set(true);
 332             sink.setLevel(loggerLevel);
 333         } finally {
 334             allowAll.get().set(before);
 335         }
 336     }
 337 
 338     // Calls the methods defined on LogProducer and verify the
 339     // parameters received by the underlying logger.
 340     private static void testLogger(LoggerFinder provider,
 341             Map<PlatformLogger, String> loggerDescMap,
 342             String name,
 343             ResourceBundle loggerBundle,
 344             PlatformLogger logger,
 345             java.util.logging.Logger sink) throws Exception {
 346 
 347         System.out.println("Testing " + loggerDescMap.get(logger));
 348         final java.util.logging.Level OFF = java.util.logging.Level.OFF;
 349 
 350         Foo foo = new Foo();
 351         String fooMsg = foo.toString();
 352         System.out.println("\tlogger.<level>(fooMsg)");
 353         for (java.util.logging.Level loggerLevel : julLevels) {
 354             setLevel(sink, loggerLevel);
 355             for (java.util.logging.Level messageLevel : julPlatformLevels) {
 356                 LogEvent expected =
 357                         LogEvent.of(
 358                             sequencer.get(),
 359                             loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
 360                             name, messageLevel, loggerBundle,
 361                             fooMsg, (Throwable)null, (Object[])null);
 362                 String desc2 = "logger." + messageLevel.toString().toLowerCase()
 363                         + "(fooMsg): loggerLevel="
 364                         + loggerLevel+", messageLevel="+messageLevel;
 365                 if (messageLevel == java.util.logging.Level.FINEST) {
 366                     logger.finest(fooMsg);
 367                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 368                 } else if (messageLevel == java.util.logging.Level.FINER) {
 369                     logger.finer(fooMsg);
 370                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 371                 } else if (messageLevel == java.util.logging.Level.FINE) {
 372                     logger.fine(fooMsg);
 373                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 374                 } else if (messageLevel == java.util.logging.Level.CONFIG) {
 375                     logger.config(fooMsg);
 376                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 377                 } else if (messageLevel == java.util.logging.Level.INFO) {
 378                     logger.info(fooMsg);
 379                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 380                 } else if (messageLevel == java.util.logging.Level.WARNING) {
 381                     logger.warning(fooMsg);
 382                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 383                 } else if (messageLevel == java.util.logging.Level.SEVERE) {
 384                     logger.severe(fooMsg);
 385                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 386                 }
 387             }
 388         }
 389 
 390         Throwable thrown = new Exception("OK: log me!");
 391         System.out.println("\tlogger.<level>(msg, thrown)");
 392         for (java.util.logging.Level loggerLevel : julLevels) {
 393             setLevel(sink, loggerLevel);
 394             for (java.util.logging.Level messageLevel :julPlatformLevels) {
 395                 LogEvent expected =
 396                         LogEvent.of(
 397                             sequencer.get(),
 398                             loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
 399                             name, messageLevel, loggerBundle,
 400                             fooMsg, thrown, (Object[])null);
 401                 String desc2 = "logger." + messageLevel.toString().toLowerCase()
 402                         + "(msg, thrown): loggerLevel="
 403                         + loggerLevel+", messageLevel="+messageLevel;
 404                 if (messageLevel == java.util.logging.Level.FINEST) {
 405                     logger.finest(fooMsg, thrown);
 406                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 407                 } else if (messageLevel == java.util.logging.Level.FINER) {
 408                     logger.finer(fooMsg, thrown);
 409                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 410                 } else if (messageLevel == java.util.logging.Level.FINE) {
 411                     logger.fine(fooMsg, thrown);
 412                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 413                 } else if (messageLevel == java.util.logging.Level.CONFIG) {
 414                     logger.config(fooMsg, thrown);
 415                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 416                 } else if (messageLevel == java.util.logging.Level.INFO) {
 417                     logger.info(fooMsg, thrown);
 418                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 419                 } else if (messageLevel == java.util.logging.Level.WARNING) {
 420                     logger.warning(fooMsg, thrown);
 421                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 422                 } else if (messageLevel == java.util.logging.Level.SEVERE) {
 423                     logger.severe(fooMsg, thrown);
 424                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 425                 }
 426             }
 427         }
 428 
 429         String format = "two params [{1} {2}]";
 430         Object arg1 = foo;
 431         Object arg2 = fooMsg;
 432         System.out.println("\tlogger.<level>(format, arg1, arg2)");
 433         for (java.util.logging.Level loggerLevel : julLevels) {
 434             setLevel(sink, loggerLevel);
 435             for (java.util.logging.Level messageLevel : julPlatformLevels) {
 436                 LogEvent expected =
 437                         LogEvent.of(
 438                             sequencer.get(),
 439                             loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
 440                             name, messageLevel, loggerBundle,
 441                             format, (Throwable)null, foo, fooMsg);
 442                 String desc2 = "logger." + messageLevel.toString().toLowerCase()
 443                         + "(format, foo, fooMsg): loggerLevel="
 444                         + loggerLevel+", messageLevel="+messageLevel;
 445                 if (messageLevel == java.util.logging.Level.FINEST) {
 446                     logger.finest(format, foo, fooMsg);
 447                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 448                 } else if (messageLevel == java.util.logging.Level.FINER) {
 449                     logger.finer(format, foo, fooMsg);
 450                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 451                 } else if (messageLevel == java.util.logging.Level.FINE) {
 452                     logger.fine(format, foo, fooMsg);
 453                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 454                 } else if (messageLevel == java.util.logging.Level.CONFIG) {
 455                     logger.config(format, foo, fooMsg);
 456                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 457                 } else if (messageLevel == java.util.logging.Level.INFO) {
 458                     logger.info(format, foo, fooMsg);
 459                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 460                 } else if (messageLevel == java.util.logging.Level.WARNING) {
 461                     logger.warning(format, foo, fooMsg);
 462                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 463                 } else if (messageLevel == java.util.logging.Level.SEVERE) {
 464                     logger.severe(format, foo, fooMsg);
 465                     checkLogEvent(provider, desc2, expected, expected.isLoggable);
 466                 }
 467             }
 468         }
 469 
 470     }
 471 
 472     final static class PermissionsBuilder {
 473         final Permissions perms;
 474         public PermissionsBuilder() {
 475             this(new Permissions());
 476         }
 477         public PermissionsBuilder(Permissions perms) {
 478             this.perms = perms;
 479         }
 480         public PermissionsBuilder add(Permission p) {
 481             perms.add(p);
 482             return this;
 483         }
 484         public PermissionsBuilder addAll(PermissionCollection col) {
 485             if (col != null) {
 486                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 487                     perms.add(e.nextElement());
 488                 }
 489             }
 490             return this;
 491         }
 492         public Permissions toPermissions() {
 493             final PermissionsBuilder builder = new PermissionsBuilder();
 494             builder.addAll(perms);
 495             return builder.perms;
 496         }
 497     }
 498 
 499     public static class SimplePolicy extends Policy {
 500         public static final RuntimePermission LOGGERFINDER_PERMISSION =
 501                 new RuntimePermission("loggerFinder");
 502 
 503         final Permissions permissions;
 504         final Permissions withControlPermissions;
 505         final Permissions allPermissions;
 506         final ThreadLocal<AtomicBoolean> allowAll;
 507         final ThreadLocal<AtomicBoolean> allowControl;
 508         public SimplePolicy(ThreadLocal<AtomicBoolean> allowAll,
 509                 ThreadLocal<AtomicBoolean> allowControl) {
 510             this.allowAll = allowAll;
 511             this.allowControl = allowControl;
 512             permissions = new Permissions();
 513 
 514             withControlPermissions = new Permissions();
 515             withControlPermissions.add(LOGGERFINDER_PERMISSION);
 516 
 517             // these are used for configuring the test itself...
 518             allPermissions = new Permissions();
 519             allPermissions.add(new java.security.AllPermission());
 520         }
 521 
 522         @Override
 523         public boolean implies(ProtectionDomain domain, Permission permission) {
 524             if (allowAll.get().get()) return allPermissions.implies(permission);
 525             if (allowControl.get().get()) return withControlPermissions.implies(permission);
 526             return permissions.implies(permission);
 527         }
 528 
 529         @Override
 530         public PermissionCollection getPermissions(CodeSource codesource) {
 531             return new PermissionsBuilder().addAll(
 532                     allowAll.get().get() ? allPermissions :
 533                     allowControl.get().get()
 534                     ? withControlPermissions : permissions).toPermissions();
 535         }
 536 
 537         @Override
 538         public PermissionCollection getPermissions(ProtectionDomain domain) {
 539             return new PermissionsBuilder().addAll(
 540                     allowAll.get().get() ? allPermissions :
 541                     allowControl.get().get()
 542                     ? withControlPermissions : permissions).toPermissions();
 543         }
 544     }
 545 }