1 /* 2 * Copyright (c) 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 custom.DotHandler; 24 25 import java.io.ByteArrayOutputStream; 26 import java.io.IOException; 27 import java.io.OutputStream; 28 import java.io.PrintStream; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 import java.nio.file.Paths; 32 import java.nio.file.StandardCopyOption; 33 import java.util.List; 34 import java.util.logging.Handler; 35 import java.util.logging.Level; 36 import java.util.logging.LogManager; 37 import java.util.logging.Logger; 38 import java.util.stream.Collectors; 39 import java.util.stream.Stream; 40 41 /** 42 * @test 43 * @bug 8191033 44 * @build custom.DotHandler custom.Handler 45 * @run main/othervm BadRootLoggerHandlers CUSTOM 46 * @run main/othervm BadRootLoggerHandlers DEFAULT 47 * @author danielfuchs 48 */ 49 public class BadRootLoggerHandlers { 50 51 public static final Path SRC_DIR = 52 Paths.get(System.getProperty("test.src", "src")); 53 public static final Path USER_DIR = 54 Paths.get(System.getProperty("user.dir", ".")); 55 public static final Path CONFIG_FILE = Paths.get("badlogging.properties"); 56 57 static enum TESTS { CUSTOM, DEFAULT} 58 public static final class CustomLogManager extends LogManager { 59 @Override 60 protected void addInitialRootLoggerHandlers() {} 61 } 62 public static class SystemErr extends OutputStream { 63 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 64 final OutputStream wrapped; 65 public SystemErr(OutputStream out) { 66 this.wrapped = out; 67 } 68 69 @Override 70 public void write(int b) throws IOException { 71 baos.write(b); 72 wrapped.write(b); 73 } 74 75 public void close() throws IOException { 76 flush(); 77 super.close(); 78 } 79 80 public void flush() throws IOException { 81 super.flush(); 82 wrapped.flush(); 83 } 84 85 } 86 87 // Uncomment this to run the test on Java 8. Java 8 does not have 88 // List.of(...) 89 // static final class List { 90 // static <T> java.util.List<T> of(T... items) { 91 // return Collections.unmodifiableList(Arrays.asList(items)); 92 // } 93 // } 94 95 public static void main(String[] args) throws IOException { 96 Path initialProps = SRC_DIR.resolve(CONFIG_FILE); 97 Path loggingProps = USER_DIR.resolve(CONFIG_FILE); 98 if (args.length != 1) { 99 throw new IllegalArgumentException("expected (only) one of " + List.of(TESTS.values())); 100 } 101 102 TESTS test = TESTS.valueOf(args[0]); 103 System.setProperty("java.util.logging.config.file", loggingProps.toString()); 104 if (test == TESTS.CUSTOM) { 105 System.setProperty("java.util.logging.manager", CustomLogManager.class.getName()); 106 } 107 108 Files.copy(initialProps, loggingProps, StandardCopyOption.REPLACE_EXISTING); 109 110 SystemErr err = new SystemErr(System.err); 111 System.setErr(new PrintStream(err)); 112 113 System.out.println("Root level is: " + Logger.getLogger("").getLevel()); 114 if (Logger.getLogger("").getLevel() != Level.INFO) { 115 throw new RuntimeException("Expected root level INFO, got: " 116 + Logger.getLogger("").getLevel()); 117 } 118 119 Class<? extends LogManager> logManagerClass = 120 LogManager.getLogManager().getClass(); 121 Class<? extends LogManager> expectedClass = 122 test == TESTS.CUSTOM ? CustomLogManager.class : LogManager.class; 123 if (logManagerClass != expectedClass) { 124 throw new RuntimeException("Bad class for log manager: " + logManagerClass 125 + " expected " + expectedClass + " for " + test); 126 } 127 128 if (test == TESTS.DEFAULT) { 129 // Verify that we have two handlers. One was configured with 130 // handlers=custom.Handler, the other with 131 // .handlers=custom.DotHandler 132 // Verify that exactly one of the two handlers is a custom.Handler 133 // Verify that exactly one of the two handlers is a custom.DotHandler 134 // Verify that the two handlers have an id of '1' 135 checkHandlers(Logger.getLogger("").getHandlers(), 136 1L, 137 custom.Handler.class, 138 custom.DotHandler.class); 139 } else { 140 // Verify that we have one handler, configured with 141 // handlers=custom.Handler. 142 // Verify that it is a custom.Handler 143 // Verify that the handler have an id of '1' 144 checkHandlers(Logger.getLogger("").getHandlers(), 145 1L, 146 custom.Handler.class); 147 148 } 149 150 // DEFAULT: The log message "hi" should appear twice on the console. 151 // CUSTOM: The log message "hi" should appear twice on the console. 152 // We don't check that. This is just for log analysis in case 153 // of test failure. 154 Logger.getAnonymousLogger().info("hi (" + test +")"); 155 156 // Change the root logger level to FINE in the properties file 157 // and reload the configuration. 158 Files.write(loggingProps, 159 Files.lines(initialProps) 160 .map((s) -> s.replace("INFO", "FINE")) 161 .collect(Collectors.toList())); 162 LogManager.getLogManager().readConfiguration(); 163 164 System.out.println("Root level is: " + Logger.getLogger("").getLevel()); 165 if (Logger.getLogger("").getLevel() != Level.FINE) { 166 throw new RuntimeException("Expected root level FINE, got: " 167 + Logger.getLogger("").getLevel()); 168 } 169 170 // Verify that we have now 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 '2' 175 checkHandlers(Logger.getLogger("").getHandlers(), 176 2L, 177 custom.Handler.class); 178 179 // The log message "there" should appear only once on the console. 180 // We don't check that. This is just for log analysis in case 181 // of test failure. 182 Logger.getAnonymousLogger().info("there!"); 183 184 // Change the root logger level to FINER in the properties file 185 // and reload the configuration. 186 Files.write(loggingProps, 187 Files.lines(initialProps) 188 .map((s) -> s.replace("INFO", "FINER")) 189 .collect(Collectors.toList())); 190 LogManager.getLogManager().readConfiguration(); 191 192 System.out.println("Root level is: " + Logger.getLogger("").getLevel()); 193 if (Logger.getLogger("").getLevel() != Level.FINER) { 194 throw new RuntimeException("Expected root level FINER, got: " 195 + Logger.getLogger("").getLevel()); 196 } 197 198 // Verify that we have only one handler, configured with 199 // handlers=custom.Handler, and that the other configured with 200 // .handlers=custom.DotHandler was ignored. 201 // Verify that the handler is a custom.Handler 202 // Verify that the handler has an id of '3' 203 checkHandlers(Logger.getLogger("").getHandlers(), 204 3L, 205 custom.Handler.class); 206 207 // The log message "done" should appear only once on the console. 208 // We don't check that. This is just for log analysis in case 209 // of test failure. 210 Logger.getAnonymousLogger().info("done!"); 211 212 byte[] errBytes = err.baos.toByteArray(); 213 String errText = new String(errBytes); 214 switch(test) { 215 case CUSTOM: 216 if (errText.contains("java.lang.ClassNotFoundException: 1" 217 + DotHandler.class.getName())) { 218 throw new RuntimeException("Error message found on System.err"); 219 } 220 System.out.println("OK: ClassNotFoundException error message not found for " + test); 221 break; 222 case DEFAULT: 223 if (!errText.contains("java.lang.ClassNotFoundException: 1" 224 + DotHandler.class.getName())) { 225 throw new RuntimeException("Error message not found on System.err"); 226 } 227 System.err.println("OK: ClassNotFoundException error message found for " + test); 228 break; 229 default: 230 throw new InternalError("unknown test case: " + test); 231 } 232 } 233 234 static void checkHandlers(Handler[] handlers, Long expectedID, Class<?>... clz) { 235 // Verify that we have the expected number of handlers. 236 if (Stream.of(handlers).count() != clz.length) { 237 throw new RuntimeException("Expected " + clz.length + " handlers, got: " 238 + List.of(Logger.getLogger("").getHandlers())); 239 } 240 for (Class<?> cl : clz) { 241 // Verify that the handlers are of the expected class. 242 // For each class, we should have exactly one handler 243 // of that class. 244 if (Stream.of(handlers) 245 .map(Object::getClass) 246 .filter(cl::equals) 247 .count() != 1) { 248 throw new RuntimeException("Expected one " + cl +", got: " 249 + List.of(Logger.getLogger("").getHandlers())); 250 } 251 } 252 // Verify that all handlers have the expected ID 253 if (Stream.of(Logger.getLogger("").getHandlers()) 254 .map(RootLoggerHandlers::getId) 255 .filter(expectedID::equals) 256 .count() != clz.length) { 257 throw new RuntimeException("Expected ids to be " + expectedID + ", got: " 258 + List.of(Logger.getLogger("").getHandlers())); 259 } 260 } 261 262 static long getId(Handler h) { 263 if (h instanceof custom.Handler) { 264 return ((custom.Handler)h).id; 265 } 266 if (h instanceof custom.DotHandler) { 267 return ((custom.DotHandler)h).id; 268 } 269 return -1; 270 } 271 }