2531 * <p> 2532 * It is recommended that listeners do not throw errors or exceptions. 2533 * 2534 * If a listener terminates with an uncaught error or exception then 2535 * the first exception will be propagated to the caller of 2536 * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)}) 2537 * after all listeners have been invoked. 2538 * 2539 * @implNote If more than one listener terminates with an uncaught error or 2540 * exception, an implementation may record the additional errors or 2541 * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable) 2542 * suppressed exceptions}. 2543 * 2544 * @param listener A configuration listener that will be invoked after the 2545 * configuration changed. 2546 * @return This LogManager. 2547 * @throws SecurityException if a security manager exists and if the 2548 * caller does not have LoggingPermission("control"). 2549 * @throws NullPointerException if the listener is null. 2550 * 2551 * @since 1.9 2552 */ 2553 public LogManager addConfigurationListener(Runnable listener) { 2554 final Runnable r = Objects.requireNonNull(listener); 2555 checkPermission(); 2556 final SecurityManager sm = System.getSecurityManager(); 2557 final AccessControlContext acc = 2558 sm == null ? null : AccessController.getContext(); 2559 final PrivilegedAction<Void> pa = 2560 acc == null ? null : () -> { r.run() ; return null; }; 2561 final Runnable pr = 2562 acc == null ? r : () -> AccessController.doPrivileged(pa, acc); 2563 // Will do nothing if already registered. 2564 listeners.putIfAbsent(r, pr); 2565 return this; 2566 } 2567 2568 /** 2569 * Removes a previously registered configuration listener. 2570 * 2571 * Returns silently if the listener is not found. 2572 * 2573 * @param listener the configuration listener to remove. 2574 * @throws NullPointerException if the listener is null. 2575 * @throws SecurityException if a security manager exists and if the 2576 * caller does not have LoggingPermission("control"). 2577 * 2578 * @since 1.9 2579 */ 2580 public void removeConfigurationListener(Runnable listener) { 2581 final Runnable key = Objects.requireNonNull(listener); 2582 checkPermission(); 2583 listeners.remove(key); 2584 } 2585 2586 private void invokeConfigurationListeners() { 2587 Throwable t = null; 2588 2589 // We're using an IdentityHashMap because we want to compare 2590 // keys using identity (==). 2591 // We don't want to loop within a block synchronized on 'listeners' 2592 // to avoid invoking listeners from yet another synchronized block. 2593 // So we're taking a snapshot of the values list to avoid the risk of 2594 // ConcurrentModificationException while looping. 2595 // 2596 for (Runnable c : listeners.values().toArray(new Runnable[0])) { 2597 try { 2598 c.run(); | 2531 * <p> 2532 * It is recommended that listeners do not throw errors or exceptions. 2533 * 2534 * If a listener terminates with an uncaught error or exception then 2535 * the first exception will be propagated to the caller of 2536 * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)}) 2537 * after all listeners have been invoked. 2538 * 2539 * @implNote If more than one listener terminates with an uncaught error or 2540 * exception, an implementation may record the additional errors or 2541 * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable) 2542 * suppressed exceptions}. 2543 * 2544 * @param listener A configuration listener that will be invoked after the 2545 * configuration changed. 2546 * @return This LogManager. 2547 * @throws SecurityException if a security manager exists and if the 2548 * caller does not have LoggingPermission("control"). 2549 * @throws NullPointerException if the listener is null. 2550 * 2551 * @since 9 2552 */ 2553 public LogManager addConfigurationListener(Runnable listener) { 2554 final Runnable r = Objects.requireNonNull(listener); 2555 checkPermission(); 2556 final SecurityManager sm = System.getSecurityManager(); 2557 final AccessControlContext acc = 2558 sm == null ? null : AccessController.getContext(); 2559 final PrivilegedAction<Void> pa = 2560 acc == null ? null : () -> { r.run() ; return null; }; 2561 final Runnable pr = 2562 acc == null ? r : () -> AccessController.doPrivileged(pa, acc); 2563 // Will do nothing if already registered. 2564 listeners.putIfAbsent(r, pr); 2565 return this; 2566 } 2567 2568 /** 2569 * Removes a previously registered configuration listener. 2570 * 2571 * Returns silently if the listener is not found. 2572 * 2573 * @param listener the configuration listener to remove. 2574 * @throws NullPointerException if the listener is null. 2575 * @throws SecurityException if a security manager exists and if the 2576 * caller does not have LoggingPermission("control"). 2577 * 2578 * @since 9 2579 */ 2580 public void removeConfigurationListener(Runnable listener) { 2581 final Runnable key = Objects.requireNonNull(listener); 2582 checkPermission(); 2583 listeners.remove(key); 2584 } 2585 2586 private void invokeConfigurationListeners() { 2587 Throwable t = null; 2588 2589 // We're using an IdentityHashMap because we want to compare 2590 // keys using identity (==). 2591 // We don't want to loop within a block synchronized on 'listeners' 2592 // to avoid invoking listeners from yet another synchronized block. 2593 // So we're taking a snapshot of the values list to avoid the risk of 2594 // ConcurrentModificationException while looping. 2595 // 2596 for (Runnable c : listeners.values().toArray(new Runnable[0])) { 2597 try { 2598 c.run(); |