1 /*
   2  * Copyright (c) 2017, 2018, 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.IOException;
  24 import java.nio.file.CopyOption;
  25 import java.nio.file.Files;
  26 import java.nio.file.Path;
  27 import java.nio.file.Paths;
  28 import java.nio.file.StandardCopyOption;
  29 import java.util.Arrays;
  30 import java.util.Collections;
  31 import java.util.List;
  32 import java.util.logging.Handler;
  33 import java.util.logging.Level;
  34 import java.util.logging.LogManager;
  35 import java.util.logging.Logger;
  36 import java.util.stream.Collectors;
  37 import java.util.stream.Stream;
  38 
  39 /**
  40  * @test
  41  * @bug 8191033
  42  * @build custom.DotHandler custom.Handler
  43  * @run main/othervm RootLoggerHandlers
  44  * @run main/othervm/java.security.policy==test.policy RootLoggerHandlers
  45  * @author danielfuchs
  46  */
  47 public class RootLoggerHandlers {
  48 
  49     public static final Path SRC_DIR =
  50             Paths.get(System.getProperty("test.src", "src"));
  51     public static final Path USER_DIR =
  52             Paths.get(System.getProperty("user.dir", "."));
  53     public static final Path CONFIG_FILE = Paths.get("logging.properties");
  54 
  55     // Uncomment this to run the test on Java 8. Java 8 does not have
  56     // List.of(...)
  57     //    static final class List {
  58     //        static <T> java.util.List<T> of(T... items) {
  59     //            return Collections.unmodifiableList(Arrays.asList(items));
  60     //        }
  61     //    }
  62 
  63     public static void main(String[] args) throws IOException {
  64         Path initialProps = SRC_DIR.resolve(CONFIG_FILE);
  65         Path loggingProps = USER_DIR.resolve(CONFIG_FILE);
  66         System.setProperty("java.util.logging.config.file", loggingProps.toString());
  67         Files.copy(initialProps, loggingProps, StandardCopyOption.REPLACE_EXISTING);
  68         System.out.println("Root level is: " + Logger.getLogger("").getLevel());
  69         if (Logger.getLogger("").getLevel() != Level.INFO) {
  70             throw new RuntimeException("Expected root level INFO, got: "
  71                                         + Logger.getLogger("").getLevel());
  72         }
  73         // Verify that we have two handlers. One was configured with
  74         // handlers=custom.Handler, the other with
  75         // .handlers=custom.DotHandler
  76         // Verify that exactly one of the two handlers is a custom.Handler
  77         // Verify that exactly one of the two handlers is a custom.DotHandler
  78         // Verify that the two handlers has an id of '1'
  79         checkHandlers(Logger.getLogger(""),
  80                 Logger.getLogger("").getHandlers(),
  81                 1L,
  82                 custom.Handler.class,
  83                 custom.DotHandler.class);
  84         checkHandlers(Logger.getLogger("global"),
  85                 Logger.getGlobal().getHandlers(),
  86                 1L,
  87                 custom.GlobalHandler.class);
  88 
  89         // The log message "hi" should appear twice on the console.
  90         // We don't check that. This is just for log analysis in case
  91         // of test failure.
  92         Logger.getAnonymousLogger().info("hi");
  93 
  94         // Change the root logger level to FINE in the properties file
  95         // and reload the configuration.
  96         Files.write(loggingProps,
  97                 Files.lines(initialProps)
  98                         .map((s) -> s.replace("INFO", "FINE"))
  99                         .collect(Collectors.toList()));
 100         LogManager.getLogManager().readConfiguration();
 101 
 102         System.out.println("Root level is: " + Logger.getLogger("").getLevel());
 103         if (Logger.getLogger("").getLevel() != Level.FINE) {
 104             throw new RuntimeException("Expected root level FINE, got: "
 105                     + Logger.getLogger("").getLevel());
 106         }
 107 
 108         // Verify that we have now only one handler, configured with
 109         // handlers=custom.Handler, and that the other configured with
 110         // .handlers=custom.DotHandler was ignored.
 111         // Verify that the handler is a custom.Handler
 112         // Verify that the handler has an id of '2'
 113         checkHandlers(Logger.getLogger(""),
 114                 Logger.getLogger("").getHandlers(),
 115                 2L,
 116                 custom.Handler.class);
 117         checkHandlers(Logger.getGlobal(),
 118                 Logger.getGlobal().getHandlers(),
 119                 1L);
 120 
 121         // The log message "there" should appear only once on the console.
 122         // We don't check that. This is just for log analysis in case
 123         // of test failure.
 124         Logger.getAnonymousLogger().info("there!");
 125 
 126         // Change the root logger level to FINER in the properties file
 127         // and reload the configuration.
 128         Files.write(loggingProps,
 129                 Files.lines(initialProps)
 130                         .map((s) -> s.replace("INFO", "FINER"))
 131                         .collect(Collectors.toList()));
 132         LogManager.getLogManager().readConfiguration();
 133 
 134         System.out.println("Root level is: " + Logger.getLogger("").getLevel());
 135         if (Logger.getLogger("").getLevel() != Level.FINER) {
 136             throw new RuntimeException("Expected root level FINER, got: "
 137                     + Logger.getLogger("").getLevel());
 138         }
 139 
 140         // Verify that we have only one handler, configured with
 141         // handlers=custom.Handler, and that the other configured with
 142         // .handlers=custom.DotHandler was ignored.
 143         // Verify that the handler is a custom.Handler
 144         // Verify that the handler has an id of '3'
 145         checkHandlers(Logger.getLogger(""),
 146                 Logger.getLogger("").getHandlers(),
 147                 3L,
 148                 custom.Handler.class);
 149         checkHandlers(Logger.getGlobal(),
 150                 Logger.getGlobal().getHandlers(),
 151                 1L);
 152 
 153         LogManager.getLogManager().reset();
 154         LogManager.getLogManager().updateConfiguration((s) -> (o,n) -> n);
 155         // Verify that we have only one handler, configured with
 156         // handlers=custom.Handler, and that the other configured with
 157         // .handlers=custom.DotHandler was ignored.
 158         // Verify that the handler is a custom.Handler
 159         // Verify that the handler has an id of '4'
 160         checkHandlers(Logger.getLogger(""),
 161                 Logger.getLogger("").getHandlers(),
 162                 4L,
 163                 custom.Handler.class);
 164         checkHandlers(Logger.getGlobal(),
 165                 Logger.getGlobal().getHandlers(),
 166                 2L,
 167                 custom.GlobalHandler.class);
 168 
 169         LogManager.getLogManager().updateConfiguration((s) -> (o,n) -> n);
 170         // Verify that we have only one handler, configured with
 171         // handlers=custom.Handler, and that the other configured with
 172         // .handlers=custom.DotHandler was ignored.
 173         // Verify that the handler is a custom.Handler
 174         // Verify that the handler has an id of '4'
 175         checkHandlers(Logger.getLogger(""),
 176                 Logger.getLogger("").getHandlers(),
 177                 4L,
 178                 custom.Handler.class);
 179         checkHandlers(Logger.getGlobal(),
 180                 Logger.getGlobal().getHandlers(),
 181                 2L,
 182                 custom.GlobalHandler.class);
 183 
 184 
 185         // The log message "done" should appear only once on the console.
 186         // We don't check that. This is just for log analysis in case
 187         // of test failure.
 188         Logger.getAnonymousLogger().info("done!");
 189     }
 190 
 191     static void checkHandlers(Logger logger, Handler[] handlers, Long expectedID, Class<?>... clz) {
 192         // Verify that we have the expected number of handlers.
 193         if (Stream.of(handlers).count() != clz.length) {
 194             throw new RuntimeException("Expected " + clz.length + " handlers, got: "
 195                     + List.of(logger.getHandlers()));
 196         }
 197         for (Class<?> cl : clz) {
 198             // Verify that the handlers are of the expected class.
 199             // For each class, we should have exactly one handler
 200             // of that class.
 201             if (Stream.of(handlers)
 202                     .map(Object::getClass)
 203                     .filter(cl::equals)
 204                     .count() != 1) {
 205                 throw new RuntimeException("Expected one " + cl +", got: "
 206                         + List.of(logger.getHandlers()));
 207             }
 208         }
 209         // Verify that all handlers have the expected ID
 210         if (Stream.of(logger.getHandlers())
 211                 .map(RootLoggerHandlers::getId)
 212                 .filter(expectedID::equals)
 213                 .count() != clz.length) {
 214             throw new RuntimeException("Expected ids to be " + expectedID + ", got: "
 215                     + List.of(logger.getHandlers()));
 216         }
 217     }
 218 
 219     static long getId(Handler h) {
 220         if (h instanceof custom.Handler) {
 221             return ((custom.Handler)h).id;
 222         }
 223         if (h instanceof custom.DotHandler) {
 224             return ((custom.DotHandler)h).id;
 225         }
 226         if (h instanceof custom.GlobalHandler) {
 227             return ((custom.GlobalHandler)h).id;
 228         }
 229         return -1;
 230     }
 231 }