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