test/jdk/java/util/logging/SystemLoggerConfigTest.java
author mikael
Mon, 29 Jul 2019 09:59:04 -0700
changeset 57584 9d82a35b6ff7
parent 47216 71c04702a3d5
permissions -rw-r--r--
Merge

/*
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.ref.Reference;
import java.security.Permission;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import sun.util.logging.PlatformLogger;


/**
 * @test
 * @bug     8159245
 * @summary Tests configuration of loggers.
 * @modules java.logging/sun.util.logging.internal java.base/sun.util.logging
 * @run  main/othervm SystemLoggerConfigTest NOSECURITY
 * @run  main/othervm SystemLoggerConfigTest WITHSECURITY
 *
 * @author danielfuchs
 */
public class SystemLoggerConfigTest {

    static Logger createSystemLogger(String name) {
        return sun.util.logging.internal.LoggingProviderImpl.getLogManagerAccess()
                .demandLoggerFor(LogManager.getLogManager(), name,
                                 Thread.class.getModule());
    }

    static PlatformLogger createPlatformLogger(String name) {
        return PlatformLogger.getLogger(name);
    }

    private static void assertFalse(boolean value, String msg) {
        assertEquals(false, value, msg);
    }
    private static void assertEquals(boolean expected, boolean actual, String msg) {
        if (expected != actual) {
            throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
        }
    }
    private static void assertEquals(int expected, int actual, String msg) {
        if (expected != actual) {
            throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
        }
    }
    private static void assertEquals(long expected, long actual, String msg) {
        if (expected != actual) {
            throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
        }
    }
    private static void assertEquals(Object expected, Object actual, String msg) {
        if (!Objects.equals(expected, actual)) {
            throw new AssertionError(msg+": expected: " + expected + " actual: " + actual);
        }
    }

    static class TestHandler extends Handler {
        private final List<LogRecord> records = new CopyOnWriteArrayList<>();
        public TestHandler() {
            super();
            setLevel(Level.ALL);
        }

        @Override
        public void publish(LogRecord lr) {
            records.add(lr);
        }

        public List<LogRecord> drain() {
            List<LogRecord> list = new ArrayList<>(records);
            records.clear();
            return list;
        }

        public void close() {
            records.clear();
        }

        public void flush() {
        }

    }

    public static class TestHandler1 extends TestHandler {
        final static AtomicLong COUNT = new AtomicLong();
        public TestHandler1() {
            COUNT.incrementAndGet();
        }
    }

    public static class TestHandler2 extends TestHandler {
        final static AtomicLong COUNT = new AtomicLong();
        public TestHandler2() {
            COUNT.incrementAndGet();
        }
    }

    static enum TestCase { WITHSECURITY, NOSECURITY }

    public static void main(String[] args) {
        if (args == null || args.length == 0) {
            args = Stream.of(TestCase.values())
                    .map(String::valueOf)
                    .collect(Collectors.toList())
                    .toArray(new String[0]);
        }
        Stream.of(args)
              .map(TestCase::valueOf)
              .forEach(SystemLoggerConfigTest::launch);
    }

    public static void launch(TestCase test) {
        switch(test) {
            case WITHSECURITY:
                Policy.setPolicy(new Policy() {
                    @Override
                    public boolean implies(ProtectionDomain domain, Permission permission) {
                        return true;
                    }
                });
                System.setSecurityManager(new SecurityManager());
                break;
            case NOSECURITY:
                break;
            default:
                throw new InternalError("Unexpected enum: " + test);
        }
        try {
            test(test.name(), ".1", ".child");
            test(test.name(), ".2", "");
            testUpdateConfiguration(test.name(), ".3");
            testSetPlatformLevel(test.name(), ".4");
        } catch (IOException io) {
            throw new UncheckedIOException(io);
        }
    }

    public static void test(String name, String step, String ext)
            throws IOException {

        System.out.println("\n*** Testing " + name + step + ext);

        final String systemName1a = "system.logger.one.a." + name + step + ext;
        final String systemName1b = "system.logger.one.b." + name + step + ext;
        final String appName1a = "system.logger.one.a." + name + step;
        final String appName1b = "system.logger.one.b." + name + step;
        final String msg1a = "logger name: " + systemName1a;
        final String msg1b = "logger name: " + systemName1b;
        final String systemName2 = "system.logger.two." + name + step + ext;
        final String appName2 = "system.logger.two." + name + step;
        final String msg2 = "logger name: " + systemName2;
        final String systemName3 = "system.logger.three." + name + step + ext;
        final String appName3 = "system.logger.three." + name + step;
        final String msg3 = "logger name: " + systemName3;
        List<LogRecord> records;

        System.out.println("\n[Case #1] Creating platform logger: " + systemName1a);
        PlatformLogger system1a = createPlatformLogger(systemName1a);
        System.out.println("    Creating platform logger: " + systemName1b);
        PlatformLogger system1b = createPlatformLogger(systemName1b);
        System.out.println("    Adding handler on root logger...");
        TestHandler test1 = new TestHandler();
        Logger.getLogger("").addHandler(test1);

        System.out.println("    Creating and configuring app logger: " + appName1a
                + ", " + appName1b);
        Logger app1a = Logger.getLogger(appName1a);
        app1a.setLevel(Level.INFO);
        Logger app1b = Logger.getLogger(appName1b);
        app1b.setLevel(Level.INFO);
        assertFalse(system1a.isLoggable(PlatformLogger.Level.FINEST),
                "Unexpected level for " + system1a);
        System.out.println("    Configuring root logger...");
        Logger.getLogger("").setLevel(Level.FINEST);
        System.out.println("    Logging through system logger: " + systemName1a);
        system1a.finest(msg1a);
        Reference.reachabilityFence(app1a);
        records = test1.drain();
        assertEquals(0, records.size(), "Unexpected size for " + records.toString());
        System.out.println("    Logging through system logger: " + systemName1b);
        system1b.finest(msg1b);
        Reference.reachabilityFence(app1b);
        records = test1.drain();
        assertEquals(0, records.size(), "Unexpected size for " + records.toString());
        Logger.getLogger("system.logger.one.a").finest("system.logger.one.a");
        records = test1.drain();
        assertEquals("system.logger.one.a", records.get(0).getMessage(), "Unexpected message: ");
        Logger.getLogger("").setLevel(Level.INFO);
        Logger.getLogger("system.logger.one.a").finest("system.logger.one.a");
        records = test1.drain();
        assertEquals(0, records.size(), "Unexpected size for " + records.toString());

        Reference.reachabilityFence(system1a);
        Reference.reachabilityFence(system1b);

        System.out.println("\n[Case #2] Creating system logger: " + systemName2);
        Logger system2 = createSystemLogger(systemName2);
        System.out.println("    Creating app logger: " + appName2);
        Logger app2 = Logger.getLogger(appName2);
        System.out.println("    Configuring app logger...");
        TestHandler test2 = new TestHandler();
        app2.setLevel(Level.ALL);
        app2.setUseParentHandlers(false);
        app2.addHandler(test2);
        System.out.println("    Logging through system logger: " + systemName2);
        system2.finest(msg2);
        records = test2.drain();
        assertEquals(1, records.size(), "Unexpected size for " + records.toString());
        assertEquals(msg2, records.get(0).getMessage(), "Unexpected message: ");
        records = test1.drain();
        assertEquals(0, records.size(), "Unexpected size for " + records.toString());

        Reference.reachabilityFence(app2);
        Reference.reachabilityFence(system2);

        System.out.println("\n[Case #3] Creating app logger: " + appName3);
        Logger app3 = Logger.getLogger(appName3);
        System.out.println("    Configuring app logger...");
        TestHandler test3 = new TestHandler();
        app3.setLevel(Level.ALL);
        app3.setUseParentHandlers(false);
        app3.addHandler(test3);
        System.out.println("    Creating system logger: " + systemName3);
        Logger system3 = createSystemLogger(systemName3);
        System.out.println("    Logging through system logger: " + systemName3);
        system3.finest(msg3);
        records = test3.drain();
        assertEquals(1, records.size(), "Unexpected size for " + records.toString());
        assertEquals(msg3, records.get(0).getMessage(), "Unexpected message: ");
        records = test1.drain();
        assertEquals(0, records.size(), "Unexpected size for " + records.toString());

        Reference.reachabilityFence(app3);
        Reference.reachabilityFence(system3);
        System.gc();

    }

    @SuppressWarnings("deprecated")
    static void setPlatformLevel(PlatformLogger logger, PlatformLogger.Level level) {
        logger.setLevel(level);
    }

    public static void testSetPlatformLevel(String name, String step) {
        System.out.println("\n*** Testing PlatformLogger.setLevel " + name + step);

        System.out.println("\n[Case #5] Creating app logger: " + name + step);
        // this should return named logger in the global context
        Logger foo = Logger.getLogger(name + step);
        foo.setLevel(Level.FINE);

        System.out.println("    Creating platform logger: " + name + step);
        PlatformLogger foo1 = PlatformLogger.getLogger(name + step);
        System.out.println("    Configuring platform logger...");
        setPlatformLevel(foo1, PlatformLogger.Level.INFO);

        System.out.println("    Checking levels...");
        assertEquals(foo.getName(), foo1.getName(), "Bad logger names");
         // both logger share the same config
        assertEquals(foo.getLevel(), Level.INFO, "Bad level for user logger");
        assertEquals(foo1.level(), PlatformLogger.Level.INFO,
                "Bad level for platform logger");

    }

    static void updateConfiguration(Properties props) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        props.store(baos, "");
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        LogManager.getLogManager().updateConfiguration(bais, (k) -> (o,n) -> n != null ? n : o);
    }

    static void readConfiguration(Properties props) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        props.store(baos, "");
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        LogManager.getLogManager().readConfiguration(bais);
    }

    // Tests that though two loggers exist, only one handler is created for the
    // pair when reading configuration.
    //
    public static void testUpdateConfiguration(String name, String step) throws IOException {

        System.out.println("\n*** Testing LogManager.updateConfiguration " + name + step);

        final String name1a = "system.logger.one.a." + name + step;
        final String name1b = "system.logger.one.b." + name + step;
        final String msg1a = "logger name: " + name1a;
        final String msg1b = "logger name: " + name1b;
        List<LogRecord> records;

        TestHandler1.COUNT.set(0);
        TestHandler2.COUNT.set(0);
        Properties props = new Properties();
        props.setProperty(name1a+".handlers", TestHandler1.class.getName());
        updateConfiguration(props);
        assertEquals(0, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        System.out.println("\n[Case #4] Creating app logger: " + name1a);
        Logger app1a = Logger.getLogger(name1a);
        System.out.println("    Configuring app logger...");
        TestHandler test1 = new TestHandler();
        app1a.setLevel(Level.ALL);
        app1a.setUseParentHandlers(false);
        app1a.addHandler(test1);
        assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        System.out.println("    Creating system logger: " + name1a);
        Logger system1a = createSystemLogger(name1a);
        assertEquals(Level.ALL, system1a.getLevel(), "Bad level for system logger " + name1a);
        System.out.println("    Logging through system logger: " + name1a);
        system1a.finest(msg1a);
        records = test1.drain();
        assertEquals(1, records.size(), "Unexpected size for " + records.toString());
        assertEquals(msg1a, records.get(0).getMessage(), "Unexpected message: ");
        records = test1.drain();
        assertEquals(0, records.size(), "Unexpected size for " + records.toString());

        assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        props.setProperty(name1a+".handlers", TestHandler2.class.getName());
        updateConfiguration(props);

        assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        updateConfiguration(props);

        assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        readConfiguration(props);

        assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        // readConfiguration reset handlers but does not recreate them
        assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        updateConfiguration(props);

        assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        LogManager.getLogManager().reset();
        updateConfiguration(props);

        assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(2, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        props.setProperty(name1a+".handlers",
                TestHandler2.class.getName() + "," + TestHandler1.class.getName());
        updateConfiguration(props);

        assertEquals(2, TestHandler1.COUNT.get(), "Bad instance count for "
                + TestHandler1.class.getName());
        assertEquals(3, TestHandler2.COUNT.get(), "Bad instance count for "
                + TestHandler2.class.getName());

        Reference.reachabilityFence(app1a);
        Reference.reachabilityFence(system1a);

        LogManager.getLogManager().readConfiguration();
        System.gc();
    }



}