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 */
23import java.io.ByteArrayInputStream;
24import java.io.ByteArrayOutputStream;
25import java.io.IOException;
26import java.io.UncheckedIOException;
27import java.lang.ref.Reference;
28import java.security.Permission;
29import java.security.Policy;
30import java.security.ProtectionDomain;
31import java.util.ArrayList;
32import java.util.List;
33import java.util.Objects;
34import java.util.Properties;
35import java.util.concurrent.CopyOnWriteArrayList;
36import java.util.concurrent.atomic.AtomicLong;
37import java.util.logging.Handler;
38import java.util.logging.Level;
39import java.util.logging.LogManager;
40import java.util.logging.LogRecord;
41import java.util.logging.Logger;
42import java.util.stream.Collectors;
43import java.util.stream.Stream;
44import 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 */
57public class SystemLoggerConfigTest {
58
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
65    static PlatformLogger createPlatformLogger(String name) {
66        return PlatformLogger.getLogger(name);
67    }
68
69    private static void assertFalse(boolean value, String msg) {
70        assertEquals(false, value, msg);
71    }
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    }
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    }
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    }
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<>();
95        public TestHandler() {
96            super();
97            setLevel(Level.ALL);
98        }
99
100        @Override
101        public void publish(LogRecord lr) {
102            records.add(lr);
103        }
104
105        public List<LogRecord> drain() {
106            List<LogRecord> list = new ArrayList<>(records);
107            records.clear();
108            return list;
109        }
110
111        public void close() {
112            records.clear();
113        }
114
115        public void flush() {
116        }
117
118    }
119
120    public static class TestHandler1 extends TestHandler {
121        final static AtomicLong COUNT = new AtomicLong();
122        public TestHandler1() {
123            COUNT.incrementAndGet();
124        }
125    }
126
127    public static class TestHandler2 extends TestHandler {
128        final static AtomicLong COUNT = new AtomicLong();
129        public TestHandler2() {
130            COUNT.incrementAndGet();
131        }
132    }
133
134    static enum TestCase { WITHSECURITY, NOSECURITY }
135
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
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
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")
276    static void setPlatformLevel(PlatformLogger logger, PlatformLogger.Level level) {
277        logger.setLevel(level);
278    }
279
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
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
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    //
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