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