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 }