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 
  24 import java.lang.reflect.Method;
  25 import java.util.Arrays;
  26 import java.util.LinkedList;
  27 import java.util.Objects;
  28 import java.util.Queue;
  29 import java.util.ResourceBundle;
  30 import java.util.concurrent.atomic.AtomicInteger;
  31 import java.util.function.Consumer;
  32 import java.util.logging.Handler;
  33 import java.util.logging.Level;
  34 import java.util.logging.LogRecord;
  35 import java.util.logging.Logger;
  36 
  37 /**
  38  * @test
  39  * @bug 8152389
  40  * @summary Verify the correct behavior of LogRecord.inferCaller() in particular
  41  *          when a message is directly logged through the root logger.
  42  * @run main/othervm TestInferCaller
  43  * @author danielfuchs
  44  */
  45 public class TestInferCaller {
  46 
  47     static final class LogEvent {
  48         public final String className;
  49         public final String methodName;
  50         public final LogRecord record;
  51 
  52         public LogEvent(String className, String methodName, LogRecord record) {
  53             this.className = className;
  54             this.methodName = methodName;
  55             this.record = record;
  56         }
  57 
  58     }
  59 
  60     static final class TestHandler extends Handler {
  61 
  62         public static final Queue<LogEvent> PUBLISHED = new LinkedList<LogEvent>();
  63 
  64         public TestHandler() {
  65             initLevel(Level.ALL);
  66         }
  67 
  68         @Override
  69         public void close() throws SecurityException { }
  70         @Override
  71         public void publish(LogRecord record) {
  72             LogEvent event = new LogEvent(record.getSourceClassName(),
  73                                           record.getSourceMethodName(),
  74                                           record);
  75             PUBLISHED.add(event);
  76         }
  77         @Override
  78         public void flush() {}
  79 
  80         private void initLevel(Level newLevel) {
  81             super.setLevel(newLevel);
  82         }
  83 
  84     }
  85 
  86     public void test1(Logger logger) {
  87         System.out.println("test1: " + loggerName(logger));
  88 
  89         AtomicInteger count = new AtomicInteger();
  90         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
  91         logger.setLevel(Level.ALL);
  92         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
  93 
  94         logger.severe("message " + count.incrementAndGet());
  95         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
  96         LogEvent event = TestHandler.PUBLISHED.remove();
  97         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
  98         checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
  99 
 100         logger.warning("message " + count.incrementAndGet());
 101         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 102         event = TestHandler.PUBLISHED.remove();
 103         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 104         checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
 105 
 106         logger.info("message " + count.incrementAndGet());
 107         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 108         event = TestHandler.PUBLISHED.remove();
 109         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 110         checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
 111 
 112         logger.config("message " + count.incrementAndGet());
 113         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 114         event = TestHandler.PUBLISHED.remove();
 115         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 116         checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
 117 
 118         logger.fine("message " + count.incrementAndGet());
 119         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 120         event = TestHandler.PUBLISHED.remove();
 121         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 122         checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
 123 
 124         logger.finer("message " + count.incrementAndGet());
 125         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 126         event = TestHandler.PUBLISHED.remove();
 127         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 128         checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
 129 
 130         logger.finest("message " + count.incrementAndGet());
 131         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 132         event = TestHandler.PUBLISHED.remove();
 133         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 134         checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
 135     }
 136 
 137     void test2(Logger logger) {
 138         AtomicInteger count = new AtomicInteger();
 139         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 140         logger.setLevel(Level.ALL);
 141         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 142 
 143         for (Level l : Arrays.asList(Level.SEVERE, Level.WARNING, Level.INFO,
 144                 Level.CONFIG, Level.FINE, Level.FINER, Level.FINEST)) {
 145             System.out.println("test2: " + loggerName(logger) + " " + l);
 146             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 147 
 148             logger.log(l, "message " + count.incrementAndGet());
 149             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 150             LogEvent event = TestHandler.PUBLISHED.remove();
 151             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 152             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 153 
 154             logger.log(l, "message " + count.incrementAndGet(), "param");
 155             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 156             event = TestHandler.PUBLISHED.remove();
 157             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 158             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 159 
 160             logger.log(l, "message " + count.incrementAndGet(), new Object[] {"foo", "bar"});
 161             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 162             event = TestHandler.PUBLISHED.remove();
 163             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 164             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 165 
 166             logger.log(l, "message " + count.incrementAndGet(), new RuntimeException());
 167             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 168             event = TestHandler.PUBLISHED.remove();
 169             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 170             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 171 
 172             // JDK 8 & 9 only (uses lambda)
 173             logger.log(l, () -> "message " + count.incrementAndGet());
 174             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 175             event = TestHandler.PUBLISHED.remove();
 176             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 177             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 178 
 179             // JDK 8 & 9 only (uses lambda)
 180             logger.log(l, new RuntimeException(), () -> "message " + count.incrementAndGet());
 181             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 182             event = TestHandler.PUBLISHED.remove();
 183             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 184             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 185 
 186             // JDK 9 only: new API
 187             logger.logrb(l, (ResourceBundle)null, "message " + count.incrementAndGet(), (Object[]) null);
 188             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 189             event = TestHandler.PUBLISHED.remove();
 190             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 191             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 192 
 193             // JDK 9 only: new API
 194             logger.logrb(l, (ResourceBundle)null, "message " + count.incrementAndGet(), new RuntimeException());
 195             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 196             event = TestHandler.PUBLISHED.remove();
 197             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 198             checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
 199 
 200         }
 201     }
 202 
 203     void test3(Logger logger) {
 204         System.out.println("test3: " + loggerName(logger));
 205         AtomicInteger count = new AtomicInteger();
 206         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 207         logger.setLevel(Level.ALL);
 208         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 209 
 210         testReflection(logger, count, "severe", "testReflection");
 211         testReflection(logger, count, "warning", "testReflection");
 212         testReflection(logger, count, "info", "testReflection");
 213         testReflection(logger, count, "config", "testReflection");
 214         testReflection(logger, count, "fine", "testReflection");
 215         testReflection(logger, count, "finer", "testReflection");
 216         testReflection(logger, count, "finest", "testReflection");
 217     }
 218 
 219     void testReflection(Logger logger, AtomicInteger count, String logm, String method) {
 220         try {
 221             Method m = Logger.class.getMethod(logm, String.class);
 222             m.invoke(logger, "message " + count.incrementAndGet());
 223             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 224             LogEvent event = TestHandler.PUBLISHED.remove();
 225             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 226             checkEvent(event, this.getClass().getName(), method, "message " + count.get());
 227 
 228             Method m2 = Method.class.getMethod("invoke", Object.class, new Object[0].getClass());
 229             m2.invoke(m, logger, new Object[] { "message " + count.incrementAndGet() });
 230             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 231             event = TestHandler.PUBLISHED.remove();
 232             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 233             checkEvent(event, this.getClass().getName(), method, "message " + count.get());
 234 
 235             m2.invoke(m2, m, new Object[] {logger, new Object[] { "message " + count.incrementAndGet() }});
 236             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 237             event = TestHandler.PUBLISHED.remove();
 238             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 239             checkEvent(event, this.getClass().getName(), method, "message " + count.get());
 240 
 241             m2.invoke(m2, m2, new Object[] { m, new Object[] {logger, new Object[] { "message " + count.incrementAndGet() }}});
 242             assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 243             event = TestHandler.PUBLISHED.remove();
 244             assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 245             checkEvent(event, this.getClass().getName(), method, "message " + count.get());
 246 
 247         } catch (Error | RuntimeException x ) {
 248             throw x;
 249         } catch (Exception x) {
 250             throw new RuntimeException(x);
 251         }
 252     }
 253 
 254     // JDK 8 & 9 only (uses lambda)
 255     public void test4(Logger logger) {
 256         System.out.println("test4: " + loggerName(logger));
 257         AtomicInteger count = new AtomicInteger();
 258         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 259         logger.setLevel(Level.ALL);
 260         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 261 
 262         logger.severe(() -> "message " + count.incrementAndGet());
 263         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 264         LogEvent event = TestHandler.PUBLISHED.remove();
 265         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 266         checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
 267 
 268         logger.warning(() -> "message " + count.incrementAndGet());
 269         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 270         event = TestHandler.PUBLISHED.remove();
 271         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 272         checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
 273 
 274         logger.info(() -> "message " + count.incrementAndGet());
 275         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 276         event = TestHandler.PUBLISHED.remove();
 277         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 278         checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
 279 
 280         logger.config(() -> "message " + count.incrementAndGet());
 281         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 282         event = TestHandler.PUBLISHED.remove();
 283         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 284         checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
 285 
 286         logger.fine(() -> "message " + count.incrementAndGet());
 287         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 288         event = TestHandler.PUBLISHED.remove();
 289         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 290         checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
 291 
 292         logger.finer(() -> "message " + count.incrementAndGet());
 293         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 294         event = TestHandler.PUBLISHED.remove();
 295         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 296         checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
 297 
 298         logger.finest(() -> "message " + count.incrementAndGet());
 299         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 300         event = TestHandler.PUBLISHED.remove();
 301         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 302         checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
 303     }
 304 
 305     // JDK 8 & 9 only  (uses lambda)
 306     void test5(Logger logger) {
 307         System.out.println("test5: " + loggerName(logger));
 308         AtomicInteger count = new AtomicInteger();
 309         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 310         logger.setLevel(Level.ALL);
 311         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 312 
 313         testLambda(count, logger::severe, "testLambda");
 314         testLambda(count, logger::warning, "testLambda");
 315         testLambda(count, logger::info, "testLambda");
 316         testLambda(count, logger::config, "testLambda");
 317         testLambda(count, logger::fine, "testLambda");
 318         testLambda(count, logger::finer, "testLambda");
 319         testLambda(count, logger::finest, "testLambda");
 320     }
 321 
 322     // JDK 8 & 9 only (uses lambda)
 323     void testLambda(AtomicInteger count, Consumer<String> logm, String method) {
 324         logm.accept("message " + count.incrementAndGet());
 325         assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
 326         LogEvent event = TestHandler.PUBLISHED.remove();
 327         assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
 328         checkEvent(event, this.getClass().getName(), method, "message " + count.get());
 329     }
 330 
 331     private static String loggerName(Logger logger) {
 332         String name = logger.getName();
 333         if (name == null) return "<anonymous>";
 334         if (name.isEmpty()) return "<RootLogger>";
 335         return "\"" + name + "\"";
 336     }
 337 
 338     public void test(Logger logger) {
 339         test1(logger);
 340         test2(logger);
 341         test3(logger);
 342 
 343         // JDK 8 & 9 only (uses lambda)
 344         test4(logger);
 345         test5(logger);
 346     }
 347 
 348     public static void main(String[] args) {
 349         TestInferCaller test = new TestInferCaller();
 350         Logger root = Logger.getLogger("");
 351         for (Handler h : root.getHandlers()) {
 352             h.setLevel(Level.OFF);
 353         }
 354         root.addHandler(new TestHandler());
 355 
 356         for (Logger logger : Arrays.asList(root, Logger.getGlobal(),
 357                 Logger.getAnonymousLogger(), Logger.getLogger("foo.bar"))) {
 358             System.out.println("Testing with: " + loggerName(logger) + " " + logger.getClass());
 359             test.test(logger);
 360         }
 361     }
 362 
 363     private static void assertEquals(int expected, int actual, String what) {
 364         if (expected != actual) {
 365             throw new RuntimeException(what
 366                     + "\n\texpected: " + expected
 367                     + "\n\tactual:   " + actual);
 368         }
 369     }
 370 
 371     private static void assertEquals(String expected, String actual, String what) {
 372         if (!Objects.equals(expected, actual)) {
 373             throw new RuntimeException(what
 374                     + "\n\texpected: " + expected
 375                     + "\n\tactual:   " + actual);
 376         }
 377     }
 378 
 379     private void checkEvent(LogEvent event, String className, String methodName, String message) {
 380         assertEquals(className, event.className, "Bad class name: ");
 381         assertEquals(className, event.record.getSourceClassName(), "Bad source class name: ");
 382         assertEquals(methodName, event.methodName, "Bad method name: ");
 383         assertEquals(methodName, event.record.getSourceMethodName(), "Bad source method name: ");
 384         assertEquals(message, event.record.getMessage(), "Bad message: ");
 385     }
 386 
 387 
 388 }