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 }