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 
  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         System.out.println("    Creating and configuring app logger: " + appName1a
 201                 + ", " + appName1b);
 202         Logger.getLogger(appName1a).setLevel(Level.INFO);
 203         Logger.getLogger(appName1b).setLevel(Level.INFO);
 204         assertFalse(system1a.isLoggable(PlatformLogger.Level.FINEST),
 205                 "Unexpected level for " + system1a);
 206         System.out.println("    Configuring root logger...");
 207         Logger.getLogger("").setLevel(Level.FINEST);
 208         System.out.println("    Logging through system logger: " + systemName1a);
 209         system1a.finest(msg1a);
 210         records = test1.drain();
 211         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
 212         System.out.println("    Logging through system logger: " + systemName1b);
 213         system1b.finest(msg1b);
 214         records = test1.drain();
 215         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
 216         Logger.getLogger("system.logger.one.a").finest("system.logger.one.a");
 217         records = test1.drain();
 218         assertEquals("system.logger.one.a", records.get(0).getMessage(), "Unexpected message: ");
 219         Logger.getLogger("").setLevel(Level.INFO);
 220         Logger.getLogger("system.logger.one.a").finest("system.logger.one.a");
 221         records = test1.drain();
 222         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
 223         Reference.reachabilityFence(system1a);
 224         Reference.reachabilityFence(system1b);
 225 
 226         System.out.println("\n[Case #2] Creating system logger: " + systemName2);
 227         Logger system2 = createSystemLogger(systemName2);
 228         System.out.println("    Creating app logger: " + appName2);
 229         Logger app2 = Logger.getLogger(appName2);
 230         System.out.println("    Configuring app logger...");
 231         TestHandler test2 = new TestHandler();
 232         app2.setLevel(Level.ALL);
 233         app2.setUseParentHandlers(false);
 234         app2.addHandler(test2);
 235         System.out.println("    Logging through system logger: " + systemName2);
 236         system2.finest(msg2);
 237         records = test2.drain();
 238         assertEquals(1, records.size(), "Unexpected size for " + records.toString());
 239         assertEquals(msg2, records.get(0).getMessage(), "Unexpected message: ");
 240         records = test1.drain();
 241         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
 242 
 243         Reference.reachabilityFence(app2);
 244         Reference.reachabilityFence(system2);
 245 
 246         System.out.println("\n[Case #3] Creating app logger: " + appName3);
 247         Logger app3 = Logger.getLogger(appName3);
 248         System.out.println("    Configuring app logger...");
 249         TestHandler test3 = new TestHandler();
 250         app3.setLevel(Level.ALL);
 251         app3.setUseParentHandlers(false);
 252         app3.addHandler(test3);
 253         System.out.println("    Creating system logger: " + systemName3);
 254         Logger system3 = createSystemLogger(systemName3);
 255         System.out.println("    Logging through system logger: " + systemName3);
 256         system3.finest(msg3);
 257         records = test3.drain();
 258         assertEquals(1, records.size(), "Unexpected size for " + records.toString());
 259         assertEquals(msg3, records.get(0).getMessage(), "Unexpected message: ");
 260         records = test1.drain();
 261         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
 262 
 263         Reference.reachabilityFence(app3);
 264         Reference.reachabilityFence(system3);
 265         System.gc();
 266 
 267     }
 268 
 269     @SuppressWarnings("deprecated")
 270     static void setPlatformLevel(PlatformLogger logger, PlatformLogger.Level level) {
 271         logger.setLevel(level);
 272     }
 273 
 274     public static void testSetPlatformLevel(String name, String step) {
 275         System.out.println("\n*** Testing PlatformLogger.setLevel " + name + step);
 276 
 277         System.out.println("\n[Case #5] Creating app logger: " + name + step);
 278         // this should return named logger in the global context
 279         Logger foo = Logger.getLogger(name + step);
 280         foo.setLevel(Level.FINE);
 281 
 282         System.out.println("    Creating platform logger: " + name + step);
 283         PlatformLogger foo1 = PlatformLogger.getLogger(name + step);
 284         System.out.println("    Configuring platform logger...");
 285         setPlatformLevel(foo1, PlatformLogger.Level.INFO);
 286 
 287         System.out.println("    Checking levels...");
 288         assertEquals(foo.getName(), foo1.getName(), "Bad logger names");
 289          // both logger share the same config
 290         assertEquals(foo.getLevel(), Level.INFO, "Bad level for user logger");
 291         assertEquals(foo1.level(), PlatformLogger.Level.INFO,
 292                 "Bad level for platform logger");
 293 
 294     }
 295 
 296     static void updateConfiguration(Properties props) throws IOException {
 297         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 298         props.store(baos, "");
 299         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
 300         LogManager.getLogManager().updateConfiguration(bais, (k) -> (o,n) -> n != null ? n : o);
 301     }
 302 
 303     static void readConfiguration(Properties props) throws IOException {
 304         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 305         props.store(baos, "");
 306         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
 307         LogManager.getLogManager().readConfiguration(bais);
 308     }
 309 
 310     // Tests that though two loggers exist, only one handler is created for the
 311     // pair when reading configuration.
 312     //
 313     public static void testUpdateConfiguration(String name, String step) throws IOException {
 314 
 315         System.out.println("\n*** Testing LogManager.updateConfiguration " + name + step);
 316 
 317         final String name1a = "system.logger.one.a." + name + step;
 318         final String name1b = "system.logger.one.b." + name + step;
 319         final String msg1a = "logger name: " + name1a;
 320         final String msg1b = "logger name: " + name1b;
 321         List<LogRecord> records;
 322 
 323         TestHandler1.COUNT.set(0);
 324         TestHandler2.COUNT.set(0);
 325         Properties props = new Properties();
 326         props.setProperty(name1a+".handlers", TestHandler1.class.getName());
 327         updateConfiguration(props);
 328         assertEquals(0, TestHandler1.COUNT.get(), "Bad instance count for "
 329                 + TestHandler1.class.getName());
 330         assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
 331                 + TestHandler2.class.getName());
 332 
 333         System.out.println("\n[Case #4] Creating app logger: " + name1a);
 334         Logger app1a = Logger.getLogger(name1a);
 335         System.out.println("    Configuring app logger...");
 336         TestHandler test1 = new TestHandler();
 337         app1a.setLevel(Level.ALL);
 338         app1a.setUseParentHandlers(false);
 339         app1a.addHandler(test1);
 340         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
 341                 + TestHandler1.class.getName());
 342         assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
 343                 + TestHandler2.class.getName());
 344 
 345         System.out.println("    Creating system logger: " + name1a);
 346         Logger system1a = createSystemLogger(name1a);
 347         assertEquals(Level.ALL, system1a.getLevel(), "Bad level for system logger " + name1a);
 348         System.out.println("    Logging through system logger: " + name1a);
 349         system1a.finest(msg1a);
 350         records = test1.drain();
 351         assertEquals(1, records.size(), "Unexpected size for " + records.toString());
 352         assertEquals(msg1a, records.get(0).getMessage(), "Unexpected message: ");
 353         records = test1.drain();
 354         assertEquals(0, records.size(), "Unexpected size for " + records.toString());
 355 
 356         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
 357                 + TestHandler1.class.getName());
 358         assertEquals(0, TestHandler2.COUNT.get(), "Bad instance count for "
 359                 + TestHandler2.class.getName());
 360 
 361         props.setProperty(name1a+".handlers", TestHandler2.class.getName());
 362         updateConfiguration(props);
 363 
 364         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
 365                 + TestHandler1.class.getName());
 366         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
 367                 + TestHandler2.class.getName());
 368 
 369         updateConfiguration(props);
 370 
 371         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
 372                 + TestHandler1.class.getName());
 373         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
 374                 + TestHandler2.class.getName());
 375 
 376         readConfiguration(props);
 377 
 378         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
 379                 + TestHandler1.class.getName());
 380         // readConfiguration reset handlers but does not recreate them
 381         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
 382                 + TestHandler2.class.getName());
 383 
 384         updateConfiguration(props);
 385 
 386         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
 387                 + TestHandler1.class.getName());
 388         assertEquals(1, TestHandler2.COUNT.get(), "Bad instance count for "
 389                 + TestHandler2.class.getName());
 390 
 391         LogManager.getLogManager().reset();
 392         updateConfiguration(props);
 393 
 394         assertEquals(1, TestHandler1.COUNT.get(), "Bad instance count for "
 395                 + TestHandler1.class.getName());
 396         assertEquals(2, TestHandler2.COUNT.get(), "Bad instance count for "
 397                 + TestHandler2.class.getName());
 398         
 399         props.setProperty(name1a+".handlers",
 400                 TestHandler2.class.getName() + "," + TestHandler1.class.getName());
 401         updateConfiguration(props);
 402 
 403         assertEquals(2, TestHandler1.COUNT.get(), "Bad instance count for "
 404                 + TestHandler1.class.getName());
 405         assertEquals(3, TestHandler2.COUNT.get(), "Bad instance count for "
 406                 + TestHandler2.class.getName());
 407 
 408         Reference.reachabilityFence(app1a);
 409         Reference.reachabilityFence(system1a);
 410 
 411         LogManager.getLogManager().readConfiguration();
 412         System.gc();
 413     }
 414 
 415 
 416 
 417 }