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.io.ByteArrayOutputStream; 24 import java.io.IOException; 25 import java.io.PrintStream; 26 import java.io.UncheckedIOException; 27 import java.security.AccessControlException; 28 import java.security.CodeSource; 29 import java.security.Permission; 30 import java.security.PermissionCollection; 31 import java.security.Permissions; 32 import java.security.Policy; 33 import java.security.ProtectionDomain; 34 import java.util.Collections; 35 import java.util.Enumeration; 36 import java.util.HashMap; 37 import java.util.Map; 38 import java.util.ResourceBundle; 39 import java.util.stream.Stream; 40 import java.util.concurrent.ConcurrentHashMap; 41 import java.util.concurrent.atomic.AtomicBoolean; 42 import java.util.concurrent.atomic.AtomicLong; 43 import java.util.function.Supplier; 44 import java.lang.System.LoggerFinder; 45 import java.lang.System.Logger; 46 import java.lang.System.Logger.Level; 47 import java.security.AccessController; 48 import java.security.PrivilegedAction; 49 import java.util.EnumSet; 50 import java.util.Iterator; 51 import java.util.Locale; 52 import java.util.ServiceConfigurationError; 53 import java.util.ServiceLoader; 54 import java.util.concurrent.atomic.AtomicReference; 55 import jdk.internal.logger.SimpleConsoleLogger; 56 57 /** 58 * @test 59 * @bug 8140364 60 * @summary JDK implementation specific unit test for LoggerFinderLoader. 61 * Tests the behavior of LoggerFinderLoader with respect to the 62 * value of the internal diagnosability switches. Also test the 63 * DefaultLoggerFinder and SimpleConsoleLogger implementation. 64 * @modules java.base/sun.util.logging 65 * java.base/jdk.internal.logger 66 * @build AccessSystemLogger LoggerFinderLoaderTest CustomSystemClassLoader 67 * @run driver AccessSystemLogger 68 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOSECURITY 69 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOPERMISSIONS 70 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest WITHPERMISSIONS 71 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOSECURITY 72 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOPERMISSIONS 73 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest WITHPERMISSIONS 74 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY 75 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS 76 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS 77 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY 78 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS 79 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS 80 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY 81 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS 82 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS 83 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOSECURITY 84 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOPERMISSIONS 85 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest WITHPERMISSIONS 86 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY 87 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS 88 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS 89 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY 90 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS 91 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS 92 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY 93 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS 94 * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS 95 * @author danielfuchs 96 */ 97 public class LoggerFinderLoaderTest { 98 99 static final RuntimePermission LOGGERFINDER_PERMISSION = 100 new RuntimePermission("loggerFinder"); 101 final static boolean VERBOSE = false; 102 static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() { 103 @Override 104 protected AtomicBoolean initialValue() { 105 return new AtomicBoolean(false); 106 } 107 }; 108 static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() { 109 @Override 110 protected AtomicBoolean initialValue() { 111 return new AtomicBoolean(false); 112 } 113 }; 114 115 final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger(); 116 static final Class<?>[] providerClass; 117 static { 118 try { 119 providerClass = new Class<?>[] { 120 ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder"), 121 ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder2") 122 }; 123 } catch (ClassNotFoundException ex) { 124 throw new ExceptionInInitializerError(ex); 125 } 126 } 127 128 /** 129 * What our test provider needs to implement. 130 */ 131 public static interface TestLoggerFinder { 132 public final static AtomicBoolean fails = new AtomicBoolean(); 133 public final static AtomicReference<String> conf = new AtomicReference<>(""); 134 public final static AtomicLong sequencer = new AtomicLong(); 135 public final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>(); 136 public final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>(); 137 138 public class LoggerImpl implements System.Logger { 139 final String name; 140 final Logger logger; 141 142 public LoggerImpl(String name, Logger logger) { 143 this.name = name; 144 this.logger = logger; 145 } 146 147 @Override 148 public String getName() { 149 return name; 150 } 151 152 @Override 153 public boolean isLoggable(Logger.Level level) { 154 return logger.isLoggable(level); 155 } 156 157 @Override 158 public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) { 159 logger.log(level, bundle, key, thrown); 160 } 161 162 @Override 163 public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) { 164 logger.log(level, bundle, format, params); 165 } 166 167 } 168 169 public Logger getLogger(String name, Class<?> caller); 170 public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller); 171 } 172 173 public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder { 174 175 static final RuntimePermission LOGGERFINDER_PERMISSION = 176 new RuntimePermission("loggerFinder"); 177 public BaseLoggerFinder() { 178 if (fails.get()) { 179 throw new RuntimeException("Simulate exception while loading provider"); 180 } 181 } 182 183 System.Logger createSimpleLogger(String name) { 184 PrivilegedAction<System.Logger> pa = () -> SimpleConsoleLogger.makeSimpleLogger(name, false); 185 return AccessController.doPrivileged(pa); 186 } 187 188 189 @Override 190 public Logger getLogger(String name, Class<?> caller) { 191 SecurityManager sm = System.getSecurityManager(); 192 if (sm != null) { 193 sm.checkPermission(LOGGERFINDER_PERMISSION); 194 } 195 PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader(); 196 ClassLoader callerLoader = AccessController.doPrivileged(pa); 197 if (callerLoader == null) { 198 return system.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name))); 199 } else { 200 return user.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name))); 201 } 202 } 203 } 204 205 public static class BaseLoggerFinder2 extends LoggerFinder implements TestLoggerFinder { 206 207 static final RuntimePermission LOGGERFINDER_PERMISSION = 208 new RuntimePermission("loggerFinder"); 209 public BaseLoggerFinder2() { 210 throw new ServiceConfigurationError("Should not come here"); 211 } 212 @Override 213 public Logger getLogger(String name, Class<?> caller) { 214 throw new ServiceConfigurationError("Should not come here"); 215 } 216 } 217 218 public static class MyBundle extends ResourceBundle { 219 220 final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>(); 221 222 @Override 223 protected Object handleGetObject(String key) { 224 if (key.contains(" (translated)")) { 225 throw new RuntimeException("Unexpected key: " + key); 226 } 227 return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)"); 228 } 229 230 @Override 231 public Enumeration<String> getKeys() { 232 return Collections.enumeration(map.keySet()); 233 } 234 235 } 236 public static class MyLoggerBundle extends MyBundle { 237 238 } 239 240 static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; 241 242 static void setSecurityManager() { 243 if (System.getSecurityManager() == null) { 244 Policy.setPolicy(new SimplePolicy(allowControl, allowAccess)); 245 System.setSecurityManager(new SecurityManager()); 246 } 247 } 248 249 static LoggerFinder getLoggerFinder(Class<?> expectedClass, 250 String errorPolicy, boolean singleton) { 251 LoggerFinder provider = null; 252 try { 253 TestLoggerFinder.sequencer.incrementAndGet(); 254 provider = LoggerFinder.getLoggerFinder(); 255 if (TestLoggerFinder.fails.get() || singleton) { 256 if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) { 257 throw new RuntimeException("Expected exception not thrown"); 258 } else if ("WARNING".equals(errorPolicy.toUpperCase(Locale.ROOT))) { 259 String warning = ErrorStream.errorStream.peek(); 260 if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) { 261 throw new RuntimeException("Expected message not found. Error stream contained: " + warning); 262 } 263 } else if ("DEBUG".equals(errorPolicy.toUpperCase(Locale.ROOT))) { 264 String warning = ErrorStream.errorStream.peek(); 265 if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) { 266 throw new RuntimeException("Expected message not found. Error stream contained: " + warning); 267 } 268 if (!warning.contains("WARNING: Exception raised trying to instantiate LoggerFinder")) { 269 throw new RuntimeException("Expected message not found. Error stream contained: " + warning); 270 } 271 if (TestLoggerFinder.fails.get()) { 272 if (!warning.contains("java.util.ServiceConfigurationError: java.lang.System$LoggerFinder: Provider LoggerFinderLoaderTest$BaseLoggerFinder could not be instantiated")) { 273 throw new RuntimeException("Expected message not found. Error stream contained: " + warning); 274 } 275 } else if (singleton) { 276 if (!warning.contains("java.util.ServiceConfigurationError: More than on LoggerFinder implementation")) { 277 throw new RuntimeException("Expected message not found. Error stream contained: " + warning); 278 } 279 } 280 } else if ("QUIET".equals(errorPolicy.toUpperCase(Locale.ROOT))) { 281 if (!ErrorStream.errorStream.peek().isEmpty()) { 282 throw new RuntimeException("Unexpected error message found: " 283 + ErrorStream.errorStream.peek()); 284 } 285 } 286 } 287 } catch(AccessControlException a) { 288 throw a; 289 } catch(Throwable t) { 290 if (TestLoggerFinder.fails.get() || singleton) { 291 // must check System.err 292 if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) { 293 provider = LoggerFinder.getLoggerFinder(); 294 } else { 295 Throwable orig = t.getCause(); 296 while (orig != null && orig.getCause() != null) orig = orig.getCause(); 297 if (orig != null) orig.printStackTrace(ErrorStream.err); 298 throw new RuntimeException("Unexpected exception: " + t, t); 299 } 300 } else { 301 throw new RuntimeException("Unexpected exception: " + t, t); 302 } 303 } 304 expectedClass.cast(provider); 305 ErrorStream.errorStream.store(); 306 System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName()); 307 return provider; 308 } 309 310 311 static class ErrorStream extends PrintStream { 312 313 static AtomicBoolean forward = new AtomicBoolean(); 314 ByteArrayOutputStream out; 315 String saved = ""; 316 public ErrorStream(ByteArrayOutputStream out) { 317 super(out); 318 this.out = out; 319 } 320 321 @Override 322 public void write(int b) { 323 super.write(b); 324 if (forward.get()) err.write(b); 325 } 326 327 @Override 328 public void write(byte[] b) throws IOException { 329 super.write(b); 330 if (forward.get()) err.write(b); 331 } 332 333 @Override 334 public void write(byte[] buf, int off, int len) { 335 super.write(buf, off, len); 336 if (forward.get()) err.write(buf, off, len); 337 } 338 339 public String peek() { 340 flush(); 341 return out.toString(); 342 } 343 344 public String drain() { 345 flush(); 346 String res = out.toString(); 347 out.reset(); 348 return res; 349 } 350 351 public void store() { 352 flush(); 353 saved = out.toString(); 354 out.reset(); 355 } 356 357 public void restore() { 358 out.reset(); 359 try { 360 out.write(saved.getBytes()); 361 } catch(IOException io) { 362 throw new UncheckedIOException(io); 363 } 364 } 365 366 static final PrintStream err = System.err; 367 static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream()); 368 } 369 370 private static StringBuilder appendProperty(StringBuilder b, String name) { 371 String value = System.getProperty(name); 372 if (value == null) return b; 373 return b.append(name).append("=").append(value).append('\n'); 374 } 375 376 public static void main(String[] args) { 377 if (args.length == 0) { 378 args = new String[] { 379 "NOSECURITY", 380 "NOPERMISSIONS", 381 "WITHPERMISSIONS" 382 }; 383 } 384 Locale.setDefault(Locale.ENGLISH); 385 System.setErr(ErrorStream.errorStream); 386 System.setProperty("jdk.logger.packages", TestLoggerFinder.LoggerImpl.class.getName()); 387 //System.setProperty("jdk.logger.finder.error", "ERROR"); 388 //System.setProperty("jdk.logger.finder.singleton", "true"); 389 //System.setProperty("test.fails", "true"); 390 TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails")); 391 StringBuilder c = new StringBuilder(); 392 appendProperty(c, "jdk.logger.packages"); 393 appendProperty(c, "jdk.logger.finder.error"); 394 appendProperty(c, "jdk.logger.finder.singleton"); 395 appendProperty(c, "test.fails"); 396 TestLoggerFinder.conf.set(c.toString()); 397 try { 398 test(args); 399 } finally { 400 try { 401 System.setErr(ErrorStream.err); 402 } catch (Error | RuntimeException x) { 403 x.printStackTrace(ErrorStream.err); 404 } 405 } 406 } 407 408 409 public static void test(String[] args) { 410 411 final String errorPolicy = System.getProperty("jdk.logger.finder.error", "WARNING"); 412 final Boolean ensureSingleton = Boolean.getBoolean("jdk.logger.finder.singleton"); 413 414 final Class<?> expectedClass = 415 TestLoggerFinder.fails.get() || ensureSingleton 416 ? jdk.internal.logger.DefaultLoggerFinder.class 417 : TestLoggerFinder.class; 418 419 System.out.println("Declared provider class: " + providerClass[0] 420 + "[" + providerClass[0].getClassLoader() + "]"); 421 422 if (!TestLoggerFinder.fails.get()) { 423 ServiceLoader<LoggerFinder> serviceLoader = 424 ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader()); 425 Iterator<LoggerFinder> iterator = serviceLoader.iterator(); 426 Object firstProvider = iterator.next(); 427 if (!firstProvider.getClass().getName().equals("LoggerFinderLoaderTest$BaseLoggerFinder")) { 428 throw new RuntimeException("Unexpected provider: " + firstProvider.getClass().getName()); 429 } 430 if (!iterator.hasNext()) { 431 throw new RuntimeException("Expected two providers"); 432 } 433 } 434 435 Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { 436 LoggerFinder provider; 437 ErrorStream.errorStream.restore(); 438 switch (testCase) { 439 case NOSECURITY: 440 System.out.println("\n*** Without Security Manager\n"); 441 System.out.println(TestLoggerFinder.conf.get()); 442 provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); 443 test(provider, true); 444 System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); 445 break; 446 case NOPERMISSIONS: 447 System.out.println("\n*** With Security Manager, without permissions\n"); 448 System.out.println(TestLoggerFinder.conf.get()); 449 setSecurityManager(); 450 try { 451 provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); 452 throw new RuntimeException("Expected exception not raised"); 453 } catch (AccessControlException x) { 454 if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { 455 throw new RuntimeException("Unexpected permission check", x); 456 } 457 final boolean control = allowControl.get().get(); 458 try { 459 allowControl.get().set(true); 460 provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); 461 } finally { 462 allowControl.get().set(control); 463 } 464 } 465 test(provider, false); 466 System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); 467 break; 468 case WITHPERMISSIONS: 469 System.out.println("\n*** With Security Manager, with control permission\n"); 470 System.out.println(TestLoggerFinder.conf.get()); 471 setSecurityManager(); 472 final boolean control = allowControl.get().get(); 473 try { 474 allowControl.get().set(true); 475 provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); 476 test(provider, true); 477 } finally { 478 allowControl.get().set(control); 479 } 480 break; 481 default: 482 throw new RuntimeException("Unknown test case: " + testCase); 483 } 484 }); 485 System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases."); 486 } 487 488 public static void test(LoggerFinder provider, boolean hasRequiredPermissions) { 489 490 ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); 491 final Map<Logger, String> loggerDescMap = new HashMap<>(); 492 493 System.Logger sysLogger = accessSystemLogger.getLogger("foo"); 494 loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")"); 495 System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle); 496 loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)"); 497 System.Logger appLogger = System.getLogger("bar"); 498 loggerDescMap.put(appLogger,"System.getLogger(\"bar\")"); 499 System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle); 500 loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)"); 501 502 testLogger(provider, loggerDescMap, "foo", null, sysLogger); 503 testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger); 504 testLogger(provider, loggerDescMap, "foo", null, appLogger); 505 testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger); 506 } 507 508 public static class Foo { 509 510 } 511 512 static void verbose(String msg) { 513 if (VERBOSE) { 514 System.out.println(msg); 515 } 516 } 517 518 // Calls the 8 methods defined on Logger and verify the 519 // parameters received by the underlying TestProvider.LoggerImpl 520 // logger. 521 private static void testLogger(LoggerFinder provider, 522 Map<Logger, String> loggerDescMap, 523 String name, 524 ResourceBundle loggerBundle, 525 Logger logger) { 526 527 System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]"); 528 AtomicLong sequencer = TestLoggerFinder.sequencer; 529 530 Foo foo = new Foo(); 531 String fooMsg = foo.toString(); 532 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 533 for (Level messageLevel : Level.values()) { 534 ErrorStream.errorStream.drain(); 535 String desc = "logger.log(messageLevel, foo): loggerLevel=" 536 + loggerLevel+", messageLevel="+messageLevel; 537 sequencer.incrementAndGet(); 538 logger.log(messageLevel, foo); 539 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 540 if (!ErrorStream.errorStream.peek().isEmpty()) { 541 throw new RuntimeException("unexpected event in queue for " 542 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 543 } 544 } else { 545 String logged = ErrorStream.errorStream.drain(); 546 if (!logged.contains("LoggerFinderLoaderTest testLogger") 547 || !logged.contains(messageLevel.getName() + ": " + fooMsg)) { 548 throw new RuntimeException("mismatch for " + desc 549 + "\n\texpected:" + "\n<<<<\n" 550 + "[date] LoggerFinderLoaderTest testLogger\n" 551 + messageLevel.getName() + " " + fooMsg 552 + "\n>>>>" 553 + "\n\t actual:" 554 + "\n<<<<\n" + logged + ">>>>\n"); 555 } else { 556 verbose("Got expected results for " 557 + desc + "\n<<<<\n" + logged + ">>>>\n"); 558 } 559 } 560 } 561 } 562 563 String msg = "blah"; 564 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 565 for (Level messageLevel : Level.values()) { 566 String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" 567 + loggerLevel+", messageLevel="+messageLevel; 568 sequencer.incrementAndGet(); 569 logger.log(messageLevel, msg); 570 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 571 if (!ErrorStream.errorStream.peek().isEmpty()) { 572 throw new RuntimeException("unexpected event in queue for " 573 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 574 } 575 } else { 576 String logged = ErrorStream.errorStream.drain(); 577 String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg); 578 if (!logged.contains("LoggerFinderLoaderTest testLogger") 579 || !logged.contains(messageLevel.getName() + ": " + msgText)) { 580 throw new RuntimeException("mismatch for " + desc 581 + "\n\texpected:" + "\n<<<<\n" 582 + "[date] LoggerFinderLoaderTest testLogger\n" 583 + messageLevel.getName() + " " + msgText 584 + "\n>>>>" 585 + "\n\t actual:" 586 + "\n<<<<\n" + logged + ">>>>\n"); 587 } else { 588 verbose("Got expected results for " 589 + desc + "\n<<<<\n" + logged + ">>>>\n"); 590 } 591 } 592 } 593 } 594 595 Supplier<String> fooSupplier = new Supplier<String>() { 596 @Override 597 public String get() { 598 return this.toString(); 599 } 600 }; 601 602 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 603 for (Level messageLevel : Level.values()) { 604 String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" 605 + loggerLevel+", messageLevel="+messageLevel; 606 sequencer.incrementAndGet(); 607 logger.log(messageLevel, fooSupplier); 608 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 609 if (!ErrorStream.errorStream.peek().isEmpty()) { 610 throw new RuntimeException("unexpected event in queue for " 611 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 612 } 613 } else { 614 String logged = ErrorStream.errorStream.drain(); 615 if (!logged.contains("LoggerFinderLoaderTest testLogger") 616 || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) { 617 throw new RuntimeException("mismatch for " + desc 618 + "\n\texpected:" + "\n<<<<\n" 619 + "[date] LoggerFinderLoaderTest testLogger\n" 620 + messageLevel.getName() + " " + fooSupplier.get() 621 + "\n>>>>" 622 + "\n\t actual:" 623 + "\n<<<<\n" + logged + ">>>>\n"); 624 } else { 625 verbose("Got expected results for " 626 + desc + "\n<<<<\n" + logged + ">>>>\n"); 627 } 628 } 629 } 630 } 631 632 633 String format = "two params [{1} {2}]"; 634 Object arg1 = foo; 635 Object arg2 = msg; 636 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 637 for (Level messageLevel : Level.values()) { 638 String desc = "logger.log(messageLevel, format, params...): loggerLevel=" 639 + loggerLevel+", messageLevel="+messageLevel; 640 sequencer.incrementAndGet(); 641 logger.log(messageLevel, format, foo, msg); 642 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 643 if (!ErrorStream.errorStream.peek().isEmpty()) { 644 throw new RuntimeException("unexpected event in queue for " 645 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 646 } 647 } else { 648 String logged = ErrorStream.errorStream.drain(); 649 String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format); 650 String text = java.text.MessageFormat.format(msgFormat, foo, msg); 651 if (!logged.contains("LoggerFinderLoaderTest testLogger") 652 || !logged.contains(messageLevel.getName() + ": " + text)) { 653 throw new RuntimeException("mismatch for " + desc 654 + "\n\texpected:" + "\n<<<<\n" 655 + "[date] LoggerFinderLoaderTest testLogger\n" 656 + messageLevel.getName() + " " + text 657 + "\n>>>>" 658 + "\n\t actual:" 659 + "\n<<<<\n" + logged + ">>>>\n"); 660 } else { 661 verbose("Got expected results for " 662 + desc + "\n<<<<\n" + logged + ">>>>\n"); 663 } 664 } 665 } 666 } 667 668 Throwable thrown = new Exception("OK: log me!"); 669 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 670 for (Level messageLevel : Level.values()) { 671 String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" 672 + loggerLevel+", messageLevel="+messageLevel; 673 sequencer.incrementAndGet(); 674 logger.log(messageLevel, msg, thrown); 675 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 676 if (!ErrorStream.errorStream.peek().isEmpty()) { 677 throw new RuntimeException("unexpected event in queue for " 678 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 679 } 680 } else { 681 String logged = ErrorStream.errorStream.drain(); 682 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 683 thrown.printStackTrace(new PrintStream(baos)); 684 String text = baos.toString(); 685 String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg); 686 if (!logged.contains("LoggerFinderLoaderTest testLogger") 687 || !logged.contains(messageLevel.getName() + ": " + msgText) 688 || !logged.contains(text)) { 689 throw new RuntimeException("mismatch for " + desc 690 + "\n\texpected:" + "\n<<<<\n" 691 + "[date] LoggerFinderLoaderTest testLogger\n" 692 + messageLevel.getName() + " " + msgText +"\n" 693 + text 694 + ">>>>" 695 + "\n\t actual:" 696 + "\n<<<<\n" + logged + ">>>>\n"); 697 } else { 698 verbose("Got expected results for " 699 + desc + "\n<<<<\n" + logged + ">>>>\n"); 700 } 701 } 702 } 703 } 704 705 706 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 707 for (Level messageLevel : Level.values()) { 708 String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" 709 + loggerLevel+", messageLevel="+messageLevel; 710 sequencer.incrementAndGet(); 711 logger.log(messageLevel, fooSupplier, thrown); 712 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 713 if (!ErrorStream.errorStream.peek().isEmpty()) { 714 throw new RuntimeException("unexpected event in queue for " 715 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 716 } 717 } else { 718 String logged = ErrorStream.errorStream.drain(); 719 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 720 thrown.printStackTrace(new PrintStream(baos)); 721 String text = baos.toString(); 722 if (!logged.contains("LoggerFinderLoaderTest testLogger") 723 || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get()) 724 || !logged.contains(text)) { 725 throw new RuntimeException("mismatch for " + desc 726 + "\n\texpected:" + "\n<<<<\n" 727 + "[date] LoggerFinderLoaderTest testLogger\n" 728 + messageLevel.getName() + " " + fooSupplier.get() +"\n" 729 + text 730 + ">>>>" 731 + "\n\t actual:" 732 + "\n<<<<\n" + logged + ">>>>\n"); 733 } else { 734 verbose("Got expected results for " 735 + desc + "\n<<<<\n" + logged + ">>>>\n"); 736 } 737 } 738 } 739 } 740 741 ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); 742 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 743 for (Level messageLevel : Level.values()) { 744 String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" 745 + loggerLevel+", messageLevel="+messageLevel; 746 sequencer.incrementAndGet(); 747 logger.log(messageLevel, bundle, format, foo, msg); 748 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 749 if (!ErrorStream.errorStream.peek().isEmpty()) { 750 throw new RuntimeException("unexpected event in queue for " 751 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 752 } 753 } else { 754 String logged = ErrorStream.errorStream.drain(); 755 String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg); 756 if (!logged.contains("LoggerFinderLoaderTest testLogger") 757 || !logged.contains(messageLevel.getName() + ": " + text)) { 758 throw new RuntimeException("mismatch for " + desc 759 + "\n\texpected:" + "\n<<<<\n" 760 + "[date] LoggerFinderLoaderTest testLogger\n" 761 + messageLevel.getName() + " " + text 762 + "\n>>>>" 763 + "\n\t actual:" 764 + "\n<<<<\n" + logged + ">>>>\n"); 765 } else { 766 verbose("Got expected results for " 767 + desc + "\n<<<<\n" + logged + ">>>>\n"); 768 } 769 } 770 } 771 } 772 773 for (Level loggerLevel : EnumSet.of(Level.INFO)) { 774 for (Level messageLevel : Level.values()) { 775 String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" 776 + loggerLevel+", messageLevel="+messageLevel; 777 sequencer.incrementAndGet(); 778 logger.log(messageLevel, bundle, msg, thrown); 779 if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { 780 if (!ErrorStream.errorStream.peek().isEmpty()) { 781 throw new RuntimeException("unexpected event in queue for " 782 + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); 783 } 784 } else { 785 String logged = ErrorStream.errorStream.drain(); 786 String textMsg = bundle.getString(msg); 787 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 788 thrown.printStackTrace(new PrintStream(baos)); 789 String text = baos.toString(); 790 if (!logged.contains("LoggerFinderLoaderTest testLogger") 791 || !logged.contains(messageLevel.getName() + ": " + textMsg) 792 || !logged.contains(text)) { 793 throw new RuntimeException("mismatch for " + desc 794 + "\n\texpected:" + "\n<<<<\n" 795 + "[date] LoggerFinderLoaderTest testLogger\n" 796 + messageLevel.getName() + " " + textMsg +"\n" 797 + text 798 + ">>>>" 799 + "\n\t actual:" 800 + "\n<<<<\n" + logged + ">>>>\n"); 801 } else { 802 verbose("Got expected results for " 803 + desc + "\n<<<<\n" + logged + ">>>>\n"); 804 } 805 } 806 } 807 } 808 809 } 810 811 final static class PermissionsBuilder { 812 final Permissions perms; 813 public PermissionsBuilder() { 814 this(new Permissions()); 815 } 816 public PermissionsBuilder(Permissions perms) { 817 this.perms = perms; 818 } 819 public PermissionsBuilder add(Permission p) { 820 perms.add(p); 821 return this; 822 } 823 public PermissionsBuilder addAll(PermissionCollection col) { 824 if (col != null) { 825 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) { 826 perms.add(e.nextElement()); 827 } 828 } 829 return this; 830 } 831 public Permissions toPermissions() { 832 final PermissionsBuilder builder = new PermissionsBuilder(); 833 builder.addAll(perms); 834 return builder.perms; 835 } 836 } 837 838 public static class SimplePolicy extends Policy { 839 final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; 840 final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); 841 842 final Permissions permissions; 843 final ThreadLocal<AtomicBoolean> allowControl; 844 final ThreadLocal<AtomicBoolean> allowAccess; 845 public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) { 846 this.allowControl = allowControl; 847 this.allowAccess = allowAccess; 848 permissions = new Permissions(); 849 permissions.add(new RuntimePermission("setIO")); 850 } 851 852 Permissions getPermissions() { 853 if (allowControl.get().get() || allowAccess.get().get()) { 854 PermissionsBuilder builder = new PermissionsBuilder() 855 .addAll(permissions); 856 if (allowControl.get().get()) { 857 builder.add(CONTROL); 858 } 859 if (allowAccess.get().get()) { 860 builder.add(ACCESS); 861 } 862 return builder.toPermissions(); 863 } 864 return permissions; 865 } 866 867 @Override 868 public boolean implies(ProtectionDomain domain, Permission permission) { 869 return getPermissions().implies(permission); 870 } 871 872 @Override 873 public PermissionCollection getPermissions(CodeSource codesource) { 874 return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); 875 } 876 877 @Override 878 public PermissionCollection getPermissions(ProtectionDomain domain) { 879 return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); 880 } 881 } 882 }