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.util.ArrayList; 24 import java.util.Collections; 25 import java.util.Enumeration; 26 import java.util.Iterator; 27 import java.util.List; 28 import java.util.concurrent.CopyOnWriteArrayList; 29 import java.util.concurrent.Phaser; 30 import java.util.concurrent.Semaphore; 31 import java.util.logging.Handler; 32 import java.util.logging.LogManager; 33 import java.util.logging.Logger; 34 import java.lang.ref.Reference; 35 36 37 /** 38 * @test 39 * @bug 7113878 8222027 40 * @summary This is not a test that will check that 7113878 is fixed, but 41 * rather a test that will invoke the modified code & try to verify 42 * that fixing 7113878 has not introduced some big regression. 43 * This test should pass, whether 7113878 is there or not. 44 * @run main/othervm TestLoggerNames 45 * @author danielfuchs 46 */ 47 public class TestLoggerNames { 48 49 static final class TestLogger extends java.util.logging.Logger { 50 51 final Semaphore sem = new Semaphore(0); 52 final Semaphore wait = new Semaphore(0); 53 54 public TestLogger(String name, String resourceBundleName) { 55 super(name, resourceBundleName); 56 } 57 58 @Override 59 public Handler[] getHandlers() { 60 boolean found = false; 61 try { 62 System.out.println("Entering "+getName()+" getHandlers()"); 63 for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { 64 if (LogManager.class.getName().equals(ste.getClassName()) 65 && "reset".equals(ste.getMethodName())) { 66 found = true; 67 System.out.println(getName()+" getHandlers() called by " + ste); 68 } 69 } 70 sem.release(); 71 try { 72 System.out.println("TestLogger: Acquiring wait for "+getName()); 73 wait.acquire(); 74 try { 75 System.out.println("TestLogger: Acquired wait for "+getName()); 76 return super.getHandlers(); 77 } finally { 78 System.out.println("TestLogger: Releasing wait for "+getName()); 79 wait.release(); 80 } 81 } finally { 82 System.out.println("Unblocking "+getName()); 83 sem.acquire(); 84 System.out.println("Unblocked "+getName()); 85 if (found) { 86 System.out.println("Reset will proceed..."); 87 } 88 } 89 } catch (InterruptedException x) { 90 throw new IllegalStateException(x); 91 } 92 } 93 } 94 95 static volatile boolean stop; 96 static volatile Throwable resetFailed; 97 static volatile Throwable checkLoggerNamesFailed; 98 static volatile Phaser phaser = new Phaser(2); 99 100 101 static void checkLoggerNames(List<Logger> loggers) { 102 Enumeration<String> names = LogManager.getLogManager().getLoggerNames(); 103 if (names instanceof Iterator) { 104 for (Iterator<?> it = Iterator.class.cast(names); it.hasNext(); ) { 105 try { 106 it.remove(); 107 throw new RuntimeException("Iterator supports remove!"); 108 } catch (UnsupportedOperationException x) { 109 System.out.println("OK: Iterator doesn't support remove."); 110 } 111 } 112 // We're not supposed to come here, but if we do then we 113 // need to rewind names... 114 names = LogManager.getLogManager().getLoggerNames(); 115 } 116 117 List<String> loggerNames = Collections.list(names); 118 if (!loggerNames.contains("")) { 119 throw new RuntimeException("\"\"" + 120 " not found in " + loggerNames); 121 } 122 if (!loggerNames.contains("global")) { 123 throw new RuntimeException("global" + 124 " not found in " + loggerNames); 125 } 126 for (Logger l : loggers) { 127 if (!loggerNames.contains(l.getName())) { 128 throw new RuntimeException(l.getName() + 129 " not found in " + loggerNames); 130 } 131 } 132 System.out.println("Got all expected logger names"); 133 } 134 135 136 public static void main(String[] args) throws InterruptedException { 137 TestLogger tl = new TestLogger("com.foo.bar.zzz", null); 138 LogManager.getLogManager().addLogger(tl); 139 try { 140 Logger.getLogger(null); 141 throw new RuntimeException("Logger.getLogger(null) didn't throw expected NPE"); 142 } catch (NullPointerException x) { 143 System.out.println("Got expected NullPointerException for Logger.getLogger(null)"); 144 } 145 List<Logger> loggers = new CopyOnWriteArrayList<>(); 146 loggers.add(Logger.getLogger("one.two.addMeAChild")); 147 loggers.add(Logger.getLogger("aaa.bbb.replaceMe")); 148 loggers.add(Logger.getLogger("bbb.aaa.addMeAChild")); 149 TestLogger test = (TestLogger)Logger.getLogger("com.foo.bar.zzz"); 150 Reference.reachabilityFence(tl); 151 loggers.add(Logger.getLogger("ddd.aaa.addMeAChild")); 152 153 checkLoggerNames(loggers); 154 155 Thread loggerNamesThread = new Thread(() -> { 156 try { 157 while (!stop) { 158 // Make a defensive copy of the list of loggers that we pass 159 // to checkLoggerNames - in order to make sure that 160 // we won't see new loggers that might appear after 161 // checkLoggerNames has called LogManager.getLoggerNames(). 162 // ('loggers' is a live list and the main thread adds and 163 // and removes loggers from it concurrently to this thread). 164 checkLoggerNames(new ArrayList<>(loggers)); 165 Thread.sleep(10); 166 if (!stop) { 167 phaser.arriveAndAwaitAdvance(); 168 } 169 } 170 } catch (Throwable t) { 171 t.printStackTrace(System.err); 172 checkLoggerNamesFailed = t; 173 // makes sure the main thread isn't going to wait 174 // forever waiting for this dead thread to arrive at the 175 // phaser. 176 phaser.arrive(); 177 } 178 }, "loggerNames"); 179 180 Thread resetThread = new Thread(() -> { 181 try { 182 System.out.println("Calling reset..."); 183 LogManager.getLogManager().reset(); 184 System.out.println("Reset done..."); 185 System.out.println("Reset again..."); 186 LogManager.getLogManager().reset(); 187 System.out.println("Reset done..."); 188 } catch(Throwable t) { 189 resetFailed = t; 190 System.err.println("Unexpected exception or error in reset Thread"); 191 t.printStackTrace(System.err); 192 } 193 }, "reset"); 194 195 resetThread.setDaemon(true); 196 resetThread.start(); 197 198 System.out.println("Waiting for reset to get handlers"); 199 test.sem.acquire(); 200 try { 201 loggerNamesThread.start(); 202 System.out.println("Reset has called getHandlers on " + test.getName()); 203 int i = 0; 204 for (Enumeration<String> e = LogManager.getLogManager().getLoggerNames(); 205 e.hasMoreElements();) { 206 String name = e.nextElement(); 207 if (name.isEmpty()) continue; 208 if (name.endsWith(".addMeAChild")) { 209 Logger l = Logger.getLogger(name+".child"); 210 loggers.add(l); 211 System.out.println("*** Added " + l.getName()); 212 i++; 213 } else if (name.endsWith("replaceMe")) { 214 Logger l = Logger.getLogger(name); 215 loggers.remove(l); 216 l = Logger.getLogger(name.replace("replaceMe", "meReplaced")); 217 loggers.add(l); 218 // Give some chance to the loggerNames thread to arrive at the 219 // phaser first. We don't really care if the logger is actually 220 // replaced this turn - or the next. 221 Thread.sleep(100); 222 System.gc(); 223 if (LogManager.getLogManager().getLogger(name) == null) { 224 // The original logger may not have been gc'ed yet, since 225 // it could still be referenced by the list being processed 226 // by the loggerNamesThread, if that thread hasn't arrived 227 // to the phaser yet... 228 System.out.println("*** "+ name 229 + " successfully replaced with " + l.getName()); 230 } 231 i++; 232 } else { 233 System.out.println("Nothing to do for logger: " + name); 234 } 235 if (checkLoggerNamesFailed != null) { 236 // The checkLoggerNames thread failed. No need to 237 // continue. 238 break; 239 } 240 // Waits for the checkLoggerNamesThread to arrive 241 phaser.arriveAndAwaitAdvance(); 242 if (i >= 3 && i++ == 3) { 243 System.out.println("Loggers are now: " + 244 Collections.list(LogManager.getLogManager().getLoggerNames())); 245 test.wait.release(); 246 test.sem.release(); 247 System.out.println("Joining " + resetThread); 248 resetThread.join(); 249 } 250 } 251 } catch (RuntimeException | InterruptedException | Error x) { 252 test.wait.release(); 253 test.sem.release(); 254 throw x; 255 } finally { 256 stop = true; 257 phaser.arriveAndDeregister(); 258 loggerNamesThread.join(); 259 loggers.clear(); 260 } 261 262 263 if (resetFailed != null || checkLoggerNamesFailed != null) { 264 RuntimeException r = new RuntimeException("Some of the concurrent threads failed"); 265 if (resetFailed != null) r.addSuppressed(resetFailed); 266 if (checkLoggerNamesFailed != null) r.addSuppressed(checkLoggerNamesFailed); 267 throw r; 268 } 269 270 } 271 272 }