/* * 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(); } }