1/* 2 * Copyright (c) 2015, 2016, 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 */ 23import java.security.CodeSource; 24import java.security.Permission; 25import java.security.PermissionCollection; 26import java.security.Permissions; 27import java.security.Policy; 28import java.security.ProtectionDomain; 29import java.util.Arrays; 30import java.util.Collections; 31import java.util.Enumeration; 32import java.util.HashMap; 33import java.util.Map; 34import java.util.Objects; 35import java.util.Queue; 36import java.util.ResourceBundle; 37import java.util.concurrent.ArrayBlockingQueue; 38import java.util.concurrent.ConcurrentHashMap; 39import java.util.concurrent.atomic.AtomicBoolean; 40import java.util.concurrent.atomic.AtomicLong; 41import java.util.logging.Handler; 42import java.util.logging.LogManager; 43import java.util.logging.LogRecord; 44import java.lang.System.LoggerFinder; 45import java.util.logging.Logger; 46import sun.util.logging.PlatformLogger; 47import sun.util.logging.internal.LoggingProviderImpl; 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 */ 57public 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 java.util.logging.Logger sink = Logger.getLogger("foo"); 252 sink.addHandler(new MyHandler()); 253 sink.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 give 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} 545