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