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