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 }