1 /*
2  * Copyright (c) 2016, 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.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.UncheckedIOException;
27 import java.lang.ref.Reference;
28 import java.security.Permission;
29 import java.security.Policy;
30 import java.security.ProtectionDomain;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Objects;
34 import java.util.Properties;
35 import java.util.concurrent.CopyOnWriteArrayList;
36 import java.util.concurrent.atomic.AtomicLong;
37 import java.util.logging.Handler;
38 import java.util.logging.Level;
39 import java.util.logging.LogManager;
40 import java.util.logging.LogRecord;
41 import java.util.logging.Logger;
42 import java.util.stream.Collectors;
43 import java.util.stream.Stream;
44 import sun.util.logging.PlatformLogger;
45 
46 
47 /**
48  * @test
49  * @bug     8159245
50  * @summary Tests configuration of loggers.
51  * @modules java.logging/sun.util.logging.internal java.base/sun.util.logging
52  * @run  main/othervm SystemLoggerConfigTest NOSECURITY
53  * @run  main/othervm SystemLoggerConfigTest WITHSECURITY
54  *
55  * @author danielfuchs
56  */
57 public class SystemLoggerConfigTest {
58 
createSystemLogger(String name)59     static Logger createSystemLogger(String name) {
60         return sun.util.logging.internal.LoggingProviderImpl.getLogManagerAccess()
61                 .demandLoggerFor(LogManager.getLogManager(), name,
62                                  Thread.class.getModule());
63     }
64 
createPlatformLogger(String name)65     static PlatformLogger createPlatformLogger(String name) {
66         return PlatformLogger.getLogger(name);
67     }
68 
assertFalse(boolean value, String msg)69     private static void assertFalse(boolean value, String msg) {
70         assertEquals(false, value, msg);
71     }
assertEquals(boolean expected, boolean actual, String msg)72     private static void assertEquals(boolean expected, boolean actual, String msg) {
73         if (expected != actual) {
74             throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
75         }
76     }
assertEquals(int expected, int actual, String msg)77     private static void assertEquals(int expected, int actual, String msg) {
78         if (expected != actual) {
79             throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
80         }
81     }
assertEquals(long expected, long actual, String msg)82     private static void assertEquals(long expected, long actual, String msg) {
83         if (expected != actual) {
84             throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
85         }
86     }
assertEquals(Object expected, Object actual, String msg)87     private static void assertEquals(Object expected, Object actual, String msg) {
88         if (!Objects.equals(expected, actual)) {
89             throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
90         }
91     }
92 
93     static class TestHandler extends Handler {
94         private final List<LogRecord> records = new CopyOnWriteArrayList<>();
TestHandler()95         public TestHandler() {
96             super();
97             setLevel(Level.ALL);
98         }
99 
100         @Override
publish(LogRecord lr)101         public void publish(LogRecord lr) {
102             records.add(lr);
103         }
104 
drain()105         public List<LogRecord> drain() {
106             List<LogRecord> list = new ArrayList<>(records);
107             records.clear();
108             return list;
109         }
110 
close()111         public void close() {
112             records.clear();
113         }
114 
flush()115         public void flush() {
116         }
117 
118     }
119 
120     public static class TestHandler1 extends TestHandler {
121         final static AtomicLong COUNT = new AtomicLong();
TestHandler1()122         public TestHandler1() {
123             COUNT.incrementAndGet();
124         }
125     }
126 
127     public static class TestHandler2 extends TestHandler {
128         final static AtomicLong COUNT = new AtomicLong();
TestHandler2()129         public TestHandler2() {
130             COUNT.incrementAndGet();
131         }
132     }
133 
134     static enum TestCase { WITHSECURITY, NOSECURITY }
135 
main(String[] args)136     public static void main(String[] args) {
137         if (args == null || args.length == 0) {
138             args = Stream.of(TestCase.values())
139                     .map(String::valueOf)
140                     .collect(Collectors.toList())
141                     .toArray(new String[0]);
142         }
143         Stream.of(args)
144               .map(TestCase::valueOf)
145               .forEach(SystemLoggerConfigTest::launch);
146     }
147 
launch(TestCase test)148     public static void launch(TestCase test) {
149         switch(test) {
150             case WITHSECURITY:
151                 Policy.setPolicy(new Policy() {
152                     @Override
153                     public boolean implies(ProtectionDomain domain, Permission permission) {
154                         return true;
155                     }
156                 });
157                 System.setSecurityManager(new SecurityManager());
158                 break;
159             case NOSECURITY:
160                 break;
161             default:
162                 throw new InternalError("Unexpected enum: " + test);
163         }
164         try {
165             test(test.name(), ".1", ".child");
166             test(test.name(), ".2", "");
167             testUpdateConfiguration(test.name(), ".3");
168             testSetPlatformLevel(test.name(), ".4");
169         } catch (IOException io) {
170             throw new UncheckedIOException(io);
171         }
172     }
173 
test(String name, String step, String ext)174     public static void test(String name, String step, String ext)
175             throws IOException {
176 
177         System.out.println("\n*** Testing " + name + step + ext);
178 
179         final String systemName1a = "system.logger.one.a." + name + step + ext;
180         final String systemName1b = "system.logger.one.b." + name + step + ext;
181         final String appName1a = "system.logger.one.a." + name + step;
182         final String appName1b = "system.logger.one.b." + name + step;
183         final String msg1a = "logger name: " + systemName1a;
184         final String msg1b = "logger name: " + systemName1b;
185         final String systemName2 = "system.logger.two." + name + step + ext;
186         final String appName2 = "system.logger.two." + name + step;
187         final String msg2 = "logger name: " + systemName2;
188         final String systemName3 = "system.logger.three." + name + step + ext;
189         final String appName3 = "system.logger.three." + name + step;
190         final String msg3 = "logger name: " + systemName3;
191         List<LogRecord> records;
192 
193         System.out.println("\n[Case #1] Creating platform logger: " + systemName1a);
194         PlatformLogger system1a = createPlatformLogger(systemName1a);
195         System.out.println("    Creating platform logger: " + systemName1b);
196         PlatformLogger system1b = createPlatformLogger(systemName1b);
197         System.out.println("    Adding handler on root logger...");
198         TestHandler test1 = new TestHandler();
199         Logger.getLogger("").addHandler(test1);
200 
201         System.out.println("    Creating and configuring app logger: " + appName1a
202                 + ", " + appName1b);
203         Logger app1a = Logger.getLogger(appName1a);
204         app1a.setLevel(Level.INFO);
205         Logger app1b = Logger.getLogger(appName1b);
206         app1b.setLevel(Level.INFO);
207         assertFalse(system1a.isLoggable(PlatformLogger.Level.FINEST),
208                 "Unexpected level for " + system1a);
209         System.out.println("    Configuring root logger...");
210         Logger.getLogger("").setLevel(Level.FINEST);
211         System.out.println("    Logging through system logger: " + systemName1a);
212         system1a.finest(msg1a);
213         Reference.reachabilityFence(app1a);
214         records = test1.drain();
215         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
216         System.out.println("    Logging through system logger: " + systemName1b);
217         system1b.finest(msg1b);
218         Reference.reachabilityFence(app1b);
219         records = test1.drain();
220         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
221         Logger.getLogger("system.logger.one.a").finest("system.logger.one.a");
222         records = test1.drain();
223         assertEquals("system.logger.one.a", records.get(0).getMessage(), "Unexpected message: ");
224         Logger.getLogger("").setLevel(Level.INFO);
225         Logger.getLogger("system.logger.one.a").finest("system.logger.one.a");
226         records = test1.drain();
227         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
228 
229         Reference.reachabilityFence(system1a);
230         Reference.reachabilityFence(system1b);
231 
232         System.out.println("\n[Case #2] Creating system logger: " + systemName2);
233         Logger system2 = createSystemLogger(systemName2);
234         System.out.println("    Creating app logger: " + appName2);
235         Logger app2 = Logger.getLogger(appName2);
236         System.out.println("    Configuring app logger...");
237         TestHandler test2 = new TestHandler();
238         app2.setLevel(Level.ALL);
239         app2.setUseParentHandlers(false);
240         app2.addHandler(test2);
241         System.out.println("    Logging through system logger: " + systemName2);
242         system2.finest(msg2);
243         records = test2.drain();
244         assertEquals(1, records.size(), "Unexpected size for " + records.toString());
245         assertEquals(msg2, records.get(0).getMessage(), "Unexpected message: ");
246         records = test1.drain();
247         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
248 
249         Reference.reachabilityFence(app2);
250         Reference.reachabilityFence(system2);
251 
252         System.out.println("\n[Case #3] Creating app logger: " + appName3);
253         Logger app3 = Logger.getLogger(appName3);
254         System.out.println("    Configuring app logger...");
255         TestHandler test3 = new TestHandler();
256         app3.setLevel(Level.ALL);
257         app3.setUseParentHandlers(false);
258         app3.addHandler(test3);
259         System.out.println("    Creating system logger: " + systemName3);
260         Logger system3 = createSystemLogger(systemName3);
261         System.out.println("    Logging through system logger: " + systemName3);
262         system3.finest(msg3);
263         records = test3.drain();
264         assertEquals(1, records.size(), "Unexpected size for " + records.toString());
265         assertEquals(msg3, records.get(0).getMessage(), "Unexpected message: ");
266         records = test1.drain();
267         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
268 
269         Reference.reachabilityFence(app3);
270         Reference.reachabilityFence(system3);
271         System.gc();
272 
273     }
274 
275     @SuppressWarnings("deprecated")
setPlatformLevel(PlatformLogger logger, PlatformLogger.Level level)276     static void setPlatformLevel(PlatformLogger logger, PlatformLogger.Level level) {
277         logger.setLevel(level);
278     }
279 
testSetPlatformLevel(String name, String step)280     public static void testSetPlatformLevel(String name, String step) {
281         System.out.println("\n*** Testing PlatformLogger.setLevel " + name + step);
282 
283         System.out.println("\n[Case #5] Creating app logger: " + name + step);
284         // this should return named logger in the global context
285         Logger foo = Logger.getLogger(name + step);
286         foo.setLevel(Level.FINE);
287 
288         System.out.println("    Creating platform logger: " + name + step);
289         PlatformLogger foo1 = PlatformLogger.getLogger(name + step);
290         System.out.println("    Configuring platform logger...");
291         setPlatformLevel(foo1, PlatformLogger.Level.INFO);
292 
293         System.out.println("    Checking levels...");
294         assertEquals(foo.getName(), foo1.getName(), "Bad logger names");
295          // both logger share the same config
296         assertEquals(foo.getLevel(), Level.INFO, "Bad level for user logger");
297         assertEquals(foo1.level(), PlatformLogger.Level.INFO,
298                 "Bad level for platform logger");
299 
300     }
301 
updateConfiguration(Properties props)302     static void updateConfiguration(Properties props) throws IOException {
303         ByteArrayOutputStream baos = new ByteArrayOutputStream();
304         props.store(baos, "");
305         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
306         LogManager.getLogManager().updateConfiguration(bais, (k) -> (o,n) -> n != null ? n : o);
307     }
308 
readConfiguration(Properties props)309     static void readConfiguration(Properties props) throws IOException {
310         ByteArrayOutputStream baos = new ByteArrayOutputStream();
311         props.store(baos, "");
312         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
313         LogManager.getLogManager().readConfiguration(bais);
314     }
315 
316     // Tests that though two loggers exist, only one handler is created for the
317     // pair when reading configuration.
318     //
testUpdateConfiguration(String name, String step)319     public static void testUpdateConfiguration(String name, String step) throws IOException {
320 
321         System.out.println("\n*** Testing LogManager.updateConfiguration " + name + step);
322 
323         final String name1a = "system.logger.one.a." + name + step;
324         final String name1b = "system.logger.one.b." + name + step;
325         final String msg1a = "logger name: " + name1a;
326         final String msg1b = "logger name: " + name1b;
327         List<LogRecord> records;
328 
329         TestHandler1.COUNT.set(0);
330         TestHandler2.COUNT.set(0);
331         Properties props = new Properties();
332         props.setProperty(name1a+".handlers", TestHandler1.class.getName());
333         updateConfiguration(props);
334         assertEquals(0, TestHandler1.COUNT.get(), "Bad instance count for "
335                 + TestHandler1.class.getName());
336         assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
337                 + TestHandler2.class.getName());
338 
339         System.out.println("\n[Case #4] Creating app logger: " + name1a);
340         Logger app1a = Logger.getLogger(name1a);
341         System.out.println("    Configuring app logger...");
342         TestHandler test1 = new TestHandler();
343         app1a.setLevel(Level.ALL);
344         app1a.setUseParentHandlers(false);
345         app1a.addHandler(test1);
346         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
347                 + TestHandler1.class.getName());
348         assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
349                 + TestHandler2.class.getName());
350 
351         System.out.println("    Creating system logger: " + name1a);
352         Logger system1a = createSystemLogger(name1a);
353         assertEquals(Level.ALL, system1a.getLevel(), "Bad level for system logger " + name1a);
354         System.out.println("    Logging through system logger: " + name1a);
355         system1a.finest(msg1a);
356         records = test1.drain();
357         assertEquals(1, records.size(), "Unexpected size for " + records.toString());
358         assertEquals(msg1a, records.get(0).getMessage(), "Unexpected message: ");
359         records = test1.drain();
360         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
361 
362         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
363                 + TestHandler1.class.getName());
364         assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
365                 + TestHandler2.class.getName());
366 
367         props.setProperty(name1a+".handlers", TestHandler2.class.getName());
368         updateConfiguration(props);
369 
370         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
371                 + TestHandler1.class.getName());
372         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
373                 + TestHandler2.class.getName());
374 
375         updateConfiguration(props);
376 
377         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
378                 + TestHandler1.class.getName());
379         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
380                 + TestHandler2.class.getName());
381 
382         readConfiguration(props);
383 
384         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
385                 + TestHandler1.class.getName());
386         // readConfiguration reset handlers but does not recreate them
387         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
388                 + TestHandler2.class.getName());
389 
390         updateConfiguration(props);
391 
392         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
393                 + TestHandler1.class.getName());
394         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
395                 + TestHandler2.class.getName());
396 
397         LogManager.getLogManager().reset();
398         updateConfiguration(props);
399 
400         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
401                 + TestHandler1.class.getName());
402         assertEquals(2, TestHandler2.COUNT.get(), "Bad instance count for "
403                 + TestHandler2.class.getName());
404 
405         props.setProperty(name1a+".handlers",
406                 TestHandler2.class.getName() + "," + TestHandler1.class.getName());
407         updateConfiguration(props);
408 
409         assertEquals(2, TestHandler1.COUNT.get(), "Bad instance count for "
410                 + TestHandler1.class.getName());
411         assertEquals(3, TestHandler2.COUNT.get(), "Bad instance count for "
412                 + TestHandler2.class.getName());
413 
414         Reference.reachabilityFence(app1a);
415         Reference.reachabilityFence(system1a);
416 
417         LogManager.getLogManager().readConfiguration();
418         System.gc();
419     }
420 
421 
422 
423 }
424