1 /* 2 * Copyright (c) 2014, 2019, 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.ByteArrayInputStream; 24 import java.io.FilePermission; 25 import java.io.IOException; 26 import java.security.AccessControlException; 27 import java.security.CodeSource; 28 import java.security.Permission; 29 import java.security.PermissionCollection; 30 import java.security.Permissions; 31 import java.security.Policy; 32 import java.security.ProtectionDomain; 33 import java.util.Arrays; 34 import java.util.Collections; 35 import java.util.ConcurrentModificationException; 36 import java.util.Enumeration; 37 import java.util.HashSet; 38 import java.util.PropertyPermission; 39 import java.util.Set; 40 import java.util.concurrent.atomic.AtomicLong; 41 import java.util.logging.LogManager; 42 import java.util.logging.LoggingPermission; 43 44 /** 45 * @test 46 * @bug 8043306 47 * @summary tests LogManager.addConfigurationListener and 48 * LogManager.removeConfigurationListener; 49 * @build TestConfigurationListeners 50 * @run main/othervm TestConfigurationListeners UNSECURE 51 * @run main/othervm TestConfigurationListeners PERMISSION 52 * @run main/othervm TestConfigurationListeners SECURE 53 * @author danielfuchs 54 */ 55 public class TestConfigurationListeners { 56 57 /** 58 * We will test add and remove ConfigurationListeners in 3 configurations. 59 * UNSECURE: No security manager. 60 * SECURE: With the security manager present - and the required 61 * LoggingPermission("control") granted. 62 * PERMISSION: With the security manager present - and the required 63 * LoggingPermission("control") *not* granted. Here we will 64 * test that the expected security permission is thrown. 65 */ 66 public static enum TestCase { 67 UNSECURE, SECURE, PERMISSION; run(String name)68 public void run(String name) throws Exception { 69 System.out.println("Running test case: " + name()); 70 switch (this) { 71 case UNSECURE: 72 testUnsecure(name); 73 break; 74 case SECURE: 75 testSecure(name); 76 break; 77 case PERMISSION: 78 testPermission(name); 79 break; 80 default: 81 throw new Error("Unknown test case: "+this); 82 } 83 } loggerName(String name)84 public String loggerName(String name) { 85 return name; 86 } 87 } 88 main(String... args)89 public static void main(String... args) throws Exception { 90 91 92 if (args == null || args.length == 0) { 93 args = new String[] { 94 TestCase.UNSECURE.name(), 95 TestCase.SECURE.name(), 96 }; 97 } 98 99 for (String testName : args) { 100 TestCase test = TestCase.valueOf(testName); 101 test.run(test.loggerName("foo.bar")); 102 } 103 } 104 105 /** 106 * Test without security manager. 107 * @param loggerName The logger to use. 108 * @throws Exception if the test fails. 109 */ testUnsecure(String loggerName)110 public static void testUnsecure(String loggerName) throws Exception { 111 if (System.getSecurityManager() != null) { 112 throw new Error("Security manager is set"); 113 } 114 test(loggerName); 115 } 116 117 /** 118 * Test with security manager. 119 * @param loggerName The logger to use. 120 * @throws Exception if the test fails. 121 */ testSecure(String loggerName)122 public static void testSecure(String loggerName) throws Exception { 123 if (System.getSecurityManager() != null) { 124 throw new Error("Security manager is already set"); 125 } 126 Policy.setPolicy(new SimplePolicy(TestCase.SECURE)); 127 System.setSecurityManager(new SecurityManager()); 128 test(loggerName); 129 } 130 131 /** 132 * Test the LoggingPermission("control") is required. 133 * @param loggerName The logger to use. 134 */ testPermission(String loggerName)135 public static void testPermission(String loggerName) { 136 TestConfigurationListener run = new TestConfigurationListener( 137 TestCase.PERMISSION.toString()); 138 if (System.getSecurityManager() != null) { 139 throw new Error("Security manager is already set"); 140 } 141 Policy.setPolicy(new SimplePolicy(TestCase.PERMISSION)); 142 System.setSecurityManager(new SecurityManager()); 143 144 try { 145 LogManager.getLogManager().addConfigurationListener(run); 146 throw new RuntimeException("addConfigurationListener: Permission not checked!"); 147 } catch (AccessControlException x) { 148 boolean ok = false; 149 if (x.getPermission() instanceof LoggingPermission) { 150 if ("control".equals(x.getPermission().getName())) { 151 System.out.println("addConfigurationListener: Got expected exception: " + x); 152 ok = true; 153 } 154 } 155 if (!ok) { 156 throw new RuntimeException("addConfigurationListener: Unexpected exception: "+x, x); 157 } 158 } 159 160 try { 161 LogManager.getLogManager().removeConfigurationListener(run); 162 throw new RuntimeException("removeConfigurationListener: Permission not checked!"); 163 } catch (AccessControlException x) { 164 boolean ok = false; 165 if (x.getPermission() instanceof LoggingPermission) { 166 if ("control".equals(x.getPermission().getName())) { 167 System.out.println("removeConfigurationListener: Got expected exception: " + x); 168 ok = true; 169 } 170 } 171 if (!ok) { 172 throw new RuntimeException("removeConfigurationListener: Unexpected exception: "+x, x); 173 } 174 } 175 try { 176 LogManager.getLogManager().addConfigurationListener(null); 177 throw new RuntimeException( 178 "addConfigurationListener(null): Expected NPE not thrown."); 179 } catch (NullPointerException npe) { 180 System.out.println("Got expected NPE: "+npe); 181 } 182 183 try { 184 LogManager.getLogManager().removeConfigurationListener(null); 185 throw new RuntimeException( 186 "removeConfigurationListener(null): Expected NPE not thrown."); 187 } catch (NullPointerException npe) { 188 System.out.println("Got expected NPE: "+npe); 189 } 190 191 192 } 193 194 195 static class TestConfigurationListener implements Runnable { 196 final AtomicLong count = new AtomicLong(0); 197 final String name; TestConfigurationListener(String name)198 TestConfigurationListener(String name) { 199 this.name = name; 200 } 201 @Override run()202 public void run() { 203 final long times = count.incrementAndGet(); 204 System.out.println("Configured \"" + name + "\": " + times); 205 } 206 } 207 208 static class ConfigurationListenerException extends RuntimeException { ConfigurationListenerException(String msg)209 public ConfigurationListenerException(String msg) { 210 super(msg); 211 } 212 213 @Override toString()214 public String toString() { 215 return this.getClass().getName() + ": " + getMessage(); 216 } 217 } 218 static class ConfigurationListenerError extends Error { ConfigurationListenerError(String msg)219 public ConfigurationListenerError(String msg) { 220 super(msg); 221 } 222 223 @Override toString()224 public String toString() { 225 return this.getClass().getName() + ": " + getMessage(); 226 } 227 } 228 229 static class ThrowingConfigurationListener extends TestConfigurationListener { 230 231 final boolean error; ThrowingConfigurationListener(String name, boolean error)232 public ThrowingConfigurationListener(String name, boolean error) { 233 super(name); 234 this.error = error; 235 } 236 237 @Override run()238 public void run() { 239 if (error) 240 throw new ConfigurationListenerError(name); 241 else 242 throw new ConfigurationListenerException(name); 243 } 244 245 @Override toString()246 public String toString() { 247 final Class<? extends Throwable> type = 248 error ? ConfigurationListenerError.class 249 : ConfigurationListenerException.class; 250 return type.getName()+ ": " + name; 251 } 252 253 } 254 expect(TestConfigurationListener listener, long value)255 private static void expect(TestConfigurationListener listener, long value) { 256 final long got = listener.count.longValue(); 257 if (got != value) { 258 throw new RuntimeException(listener.name + " expected " + value +", got " + got); 259 } 260 261 } 262 263 public interface ThrowingConsumer<T, I extends Exception> { accept(T t)264 public void accept(T t) throws I; 265 } 266 267 public static class ReadConfiguration implements ThrowingConsumer<LogManager, IOException> { 268 269 @Override accept(LogManager t)270 public void accept(LogManager t) throws IOException { 271 t.readConfiguration(); 272 } 273 274 } 275 test(String loggerName)276 public static void test(String loggerName) throws Exception { 277 System.out.println("Starting test for " + loggerName); 278 test("m.readConfiguration()", (m) -> m.readConfiguration()); 279 test("m.readConfiguration(new ByteArrayInputStream(new byte[0]))", 280 (m) -> m.readConfiguration(new ByteArrayInputStream(new byte[0]))); 281 System.out.println("Test passed for " + loggerName); 282 } 283 test(String testName, ThrowingConsumer<LogManager, IOException> readConfiguration)284 public static void test(String testName, 285 ThrowingConsumer<LogManager, IOException> readConfiguration) throws Exception { 286 287 288 System.out.println("\nBEGIN " + testName); 289 LogManager m = LogManager.getLogManager(); 290 291 final TestConfigurationListener l1 = new TestConfigurationListener("l#1"); 292 final TestConfigurationListener l2 = new TestConfigurationListener("l#2"); 293 final TestConfigurationListener l3 = new ThrowingConfigurationListener("l#3", false); 294 final TestConfigurationListener l4 = new ThrowingConfigurationListener("l#4", true); 295 final TestConfigurationListener l5 = new ThrowingConfigurationListener("l#5", false); 296 297 final Set<String> expectedExceptions = 298 Collections.unmodifiableSet( 299 new HashSet<>(Arrays.asList( 300 l3.toString(), l4.toString(), l5.toString()))); 301 302 m.addConfigurationListener(l1); 303 m.addConfigurationListener(l2); 304 expect(l1, 0); 305 expect(l2, 0); 306 307 readConfiguration.accept(m); 308 expect(l1, 1); 309 expect(l2, 1); 310 m.addConfigurationListener(l1); 311 expect(l1, 1); 312 expect(l2, 1); 313 readConfiguration.accept(m); 314 expect(l1, 2); 315 expect(l2, 2); 316 m.removeConfigurationListener(l1); 317 expect(l1, 2); 318 expect(l2, 2); 319 readConfiguration.accept(m); 320 expect(l1, 2); 321 expect(l2, 3); 322 m.removeConfigurationListener(l1); 323 expect(l1, 2); 324 expect(l2, 3); 325 readConfiguration.accept(m); 326 expect(l1, 2); 327 expect(l2, 4); 328 m.removeConfigurationListener(l2); 329 expect(l1, 2); 330 expect(l2, 4); 331 readConfiguration.accept(m); 332 expect(l1, 2); 333 expect(l2, 4); 334 335 // l1 and l2 should no longer be present: this should not fail... 336 m.removeConfigurationListener(l1); 337 m.removeConfigurationListener(l1); 338 m.removeConfigurationListener(l2); 339 m.removeConfigurationListener(l2); 340 expect(l1, 2); 341 expect(l2, 4); 342 343 readConfiguration.accept(m); 344 expect(l1, 2); 345 expect(l2, 4); 346 347 // add back l1 and l2 348 m.addConfigurationListener(l1); 349 m.addConfigurationListener(l2); 350 expect(l1, 2); 351 expect(l2, 4); 352 353 readConfiguration.accept(m); 354 expect(l1, 3); 355 expect(l2, 5); 356 357 m.removeConfigurationListener(l1); 358 m.removeConfigurationListener(l2); 359 expect(l1, 3); 360 expect(l2, 5); 361 362 readConfiguration.accept(m); 363 expect(l1, 3); 364 expect(l2, 5); 365 366 // Check the behavior when listeners throw exceptions 367 // l3, l4, and l5 will throw an error/exception. 368 // The first that is raised will be propagated, after all listeners 369 // have been invoked. The other exceptions will be added to the 370 // suppressed list. 371 // 372 // We will check that all listeners have been invoked and that we 373 // have the set of 3 exceptions expected from l3, l4, l5. 374 // 375 m.addConfigurationListener(l4); 376 m.addConfigurationListener(l1); 377 m.addConfigurationListener(l2); 378 m.addConfigurationListener(l3); 379 m.addConfigurationListener(l5); 380 381 try { 382 readConfiguration.accept(m); 383 throw new RuntimeException("Excpected exception/error not raised"); 384 } catch(ConfigurationListenerException | ConfigurationListenerError t) { 385 final Set<String> received = new HashSet<>(); 386 received.add(t.toString()); 387 for (Throwable s : t.getSuppressed()) { 388 received.add(s.toString()); 389 } 390 System.out.println("Received exceptions: " + received); 391 if (!expectedExceptions.equals(received)) { 392 throw new RuntimeException( 393 "List of received exceptions differs from expected:" 394 + "\n\texpected: " + expectedExceptions 395 + "\n\treceived: " + received); 396 } 397 } 398 expect(l1, 4); 399 expect(l2, 6); 400 401 m.removeConfigurationListener(l1); 402 m.removeConfigurationListener(l2); 403 m.removeConfigurationListener(l3); 404 m.removeConfigurationListener(l4); 405 m.removeConfigurationListener(l5); 406 readConfiguration.accept(m); 407 expect(l1, 4); 408 expect(l2, 6); 409 410 411 try { 412 m.addConfigurationListener(null); 413 throw new RuntimeException( 414 "addConfigurationListener(null): Expected NPE not thrown."); 415 } catch (NullPointerException npe) { 416 System.out.println("Got expected NPE: "+npe); 417 } 418 419 try { 420 m.removeConfigurationListener(null); 421 throw new RuntimeException( 422 "removeConfigurationListener(null): Expected NPE not thrown."); 423 } catch (NullPointerException npe) { 424 System.out.println("Got expected NPE: "+npe); 425 } 426 427 System.out.println("END " + testName+"\n"); 428 429 } 430 431 432 static final class PermissionsBuilder { 433 final Permissions perms; PermissionsBuilder()434 public PermissionsBuilder() { 435 this(new Permissions()); 436 } PermissionsBuilder(Permissions perms)437 public PermissionsBuilder(Permissions perms) { 438 this.perms = perms; 439 } add(Permission p)440 public PermissionsBuilder add(Permission p) { 441 perms.add(p); 442 return this; 443 } addAll(PermissionCollection col)444 public PermissionsBuilder addAll(PermissionCollection col) { 445 if (col != null) { 446 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) { 447 perms.add(e.nextElement()); 448 } 449 } 450 return this; 451 } toPermissions()452 public Permissions toPermissions() { 453 final PermissionsBuilder builder = new PermissionsBuilder(); 454 builder.addAll(perms); 455 return builder.perms; 456 } 457 } 458 459 public static class SimplePolicy extends Policy { 460 461 static final Policy DEFAULT_POLICY = Policy.getPolicy(); 462 463 final Permissions permissions; SimplePolicy(TestCase test)464 public SimplePolicy(TestCase test) { 465 permissions = new Permissions(); 466 if (test != TestCase.PERMISSION) { 467 permissions.add(new LoggingPermission("control", null)); 468 permissions.add(new PropertyPermission("java.util.logging.config.class", "read")); 469 permissions.add(new PropertyPermission("java.util.logging.config.file", "read")); 470 permissions.add(new PropertyPermission("java.home", "read")); 471 permissions.add(new FilePermission("<<ALL FILES>>", "read")); 472 } 473 } 474 475 @Override implies(ProtectionDomain domain, Permission permission)476 public boolean implies(ProtectionDomain domain, Permission permission) { 477 return permissions.implies(permission) || DEFAULT_POLICY.implies(domain, permission); 478 } 479 480 @Override getPermissions(CodeSource codesource)481 public PermissionCollection getPermissions(CodeSource codesource) { 482 return new PermissionsBuilder().addAll(permissions).toPermissions(); 483 } 484 485 @Override getPermissions(ProtectionDomain domain)486 public PermissionCollection getPermissions(ProtectionDomain domain) { 487 return new PermissionsBuilder().addAll(permissions).toPermissions(); 488 } 489 } 490 491 } 492