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