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