1 /*
   2  * Copyright (c) 2015, 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 /**
  25  * @test
  26  * @bug     8140364
  27  * @author  danielfuchs
  28  * @summary  JDK implementation specific unit test for JDK internal artifacts.
  29  *           This test tests all the public API methods defined in the {@link
  30  *           java.lang.System.Logger} interface, as well as all the JDK
  31  *           internal methods defined in the
  32  *           {@link sun.util.logging.PlatformLogger.Bridge}
  33  *           interface, with loggers returned by  {@link
  34  *           java.lang.System.LoggerFinder#getLogger(java.lang.String, java.lang.Class)}
  35  *           and {@link java.lang.System.LoggerFinder#getLocalizedLogger(java.lang.String,
  36  *           java.util.ResourceBundle, java.lang.Class)}
  37  *           (using both a null resource bundle and a non null resource bundle).
  38  *           It calls both the {@link java.lang.System} factory methods and
  39  *           {@link jdk.internal.logger.LazyLoggers} to obtains those loggers,
  40  *           and configure them with all possible known levels.
  41  * @modules java.base/sun.util.logging
  42  *          java.base/jdk.internal.logger
  43  *          java.logging/sun.util.logging.internal
  44  * @build LoggerFinderBackendTest SystemClassLoader
  45  * @run  main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=true LoggerFinderBackendTest
  46  * @run  main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=false LoggerFinderBackendTest
  47  */
  48 
  49 
  50 import java.lang.invoke.MethodHandle;
  51 import java.lang.invoke.MethodHandles;
  52 import java.lang.invoke.MethodHandles.Lookup;
  53 import java.lang.invoke.MethodType;
  54 import java.lang.reflect.InvocationTargetException;
  55 import java.lang.reflect.Method;
  56 import java.util.ArrayList;
  57 import java.util.Arrays;
  58 import java.util.Collections;
  59 import java.util.Enumeration;
  60 import java.util.HashMap;
  61 import java.util.LinkedHashMap;
  62 import java.util.LinkedHashSet;
  63 import java.util.List;
  64 import java.util.Map;
  65 import java.util.Objects;
  66 import java.util.ResourceBundle;
  67 import java.util.concurrent.atomic.AtomicInteger;
  68 import java.util.function.BiFunction;
  69 import java.util.function.BooleanSupplier;
  70 import java.util.function.Function;
  71 import java.util.function.Supplier;
  72 import java.lang.System.LoggerFinder;
  73 import java.util.logging.ConsoleHandler;
  74 import java.util.logging.Handler;
  75 import sun.util.logging.PlatformLogger.Level;
  76 import java.util.logging.LogManager;
  77 import java.util.logging.LogRecord;
  78 import java.util.logging.Logger;
  79 import sun.util.logging.internal.LoggingProviderImpl;
  80 import java.lang.reflect.Module;
  81 
  82 /**
  83  * @author danielfuchs
  84  */
  85 public class LoggerFinderBackendTest {
  86 
  87     // whether the implementation of Logger try to do a best
  88     // effort for logp... If the provider is not hidden, then
  89     // the logp() implementation comes from LoggerWrapper - which does a
  90     // best effort. Otherwise, it comes from the default provider
  91     // which does support logp.
  92     static final boolean BEST_EFFORT_FOR_LOGP =
  93             !Boolean.getBoolean("test.logger.hidesProvider");
  94     static final boolean VERBOSE = false;
  95 
  96     static final Class<java.lang.System.Logger> spiLoggerClass =
  97             java.lang.System.Logger.class;
  98     static final Class<java.lang.System.Logger> jdkLoggerClass =
  99             java.lang.System.Logger.class;
 100     static final Class<sun.util.logging.PlatformLogger.Bridge> bridgeLoggerClass =
 101             sun.util.logging.PlatformLogger.Bridge.class;
 102 
 103     /** Use to retrieve the log records that were produced by the JUL backend */
 104     static class LoggerTesterHandler extends Handler {
 105         public final List<LogRecord> records =
 106                 Collections.synchronizedList(new ArrayList<>());
 107 
 108         @Override
 109         public void publish(LogRecord record) {
 110             record.getSourceClassName(); record.getSourceMethodName();
 111             records.add(record);
 112         }
 113 
 114         @Override
 115         public void flush() {
 116         }
 117 
 118         @Override
 119         public void close() throws SecurityException {
 120             records.clear();
 121         }
 122 
 123         public void reset() {
 124             records.clear();
 125         }
 126     }
 127 
 128     /** The {@link LoggerTesterHandler} handler is added to the root logger. */
 129     static final LoggerTesterHandler handler = new LoggerTesterHandler();
 130     static {
 131         for (Handler h : Logger.getLogger("").getHandlers()) {
 132             if (h instanceof ConsoleHandler) {
 133                 Logger.getLogger("").removeHandler(h);
 134             }
 135         }
 136         Logger.getLogger("").addHandler(handler);
 137     }
 138 
 139     /**
 140      * A resource handler parameter that will be used when calling out the
 141      * logrb-like methods - as well as when calling the level-specific
 142      * methods that take a ResourceBundle parameter.
 143      */
 144     public static class ResourceBundeParam extends ResourceBundle {
 145         Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
 146         @Override
 147         protected Object handleGetObject(String key) {
 148             map.putIfAbsent(key, "${"+key+"}");
 149             return map.get(key);
 150         }
 151 
 152         @Override
 153         public Enumeration<String> getKeys() {
 154             return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
 155         }
 156 
 157     }
 158 
 159     final static ResourceBundle bundleParam =
 160             ResourceBundle.getBundle(ResourceBundeParam.class.getName());
 161 
 162     /**
 163      * A resource handler parameter that will be used when creating localized
 164      * loggers by calling {@link
 165      * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)}.
 166      */
 167     public static class ResourceBundeLocalized extends ResourceBundle {
 168         Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
 169         @Override
 170         protected Object handleGetObject(String key) {
 171             map.putIfAbsent(key, "Localized:${"+key+"}");
 172             return map.get(key);
 173         }
 174 
 175         @Override
 176         public Enumeration<String> getKeys() {
 177             return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
 178         }
 179 
 180     }
 181 
 182     /**
 183      * The Levels enum is used to call all the level-specific methods on
 184      * a logger instance. To minimize the amount of code it uses reflection
 185      * to do so.
 186      */
 187     static Lookup lookup = MethodHandles.lookup();
 188     public enum Levels {
 189         /** Used to call all forms of Logger.log?(SEVERE, ...) */
 190         SEVERE("severe", bridgeLoggerClass, Level.SEVERE, null, "error", false),
 191         /** Used to call all forms of Logger.log?(WARNING,...) */
 192         WARNING("warning", bridgeLoggerClass, Level.WARNING, "warning", "warning", false),
 193         /** Used to call all forms of Logger.log?(INFO,...) */
 194         INFO("info", bridgeLoggerClass, Level.INFO, "info", "info", false),
 195         /** Used to call all forms of Logger.log?(CONFIG,...) */
 196         CONFIG("config", bridgeLoggerClass, Level.CONFIG, null, "debug", false),
 197         /** Used to call all forms of Logger.log?(FINE,...) */
 198         FINE("fine", bridgeLoggerClass, Level.FINE, null, "debug", false),
 199         /** Used to call all forms of Logger.log?(FINER,...) */
 200         FINER("finer", bridgeLoggerClass, Level.FINER, null, "trace", false),
 201         /** Used to call all forms of Logger.log?(FINEST,...) */
 202         FINEST("finest", bridgeLoggerClass, Level.FINEST, null, "trace", false),
 203         ;
 204         public final String method;  // The name of the level-specific method to call
 205         public final Class<?> definingClass; // which interface j.u.logger.Logger or j.u.logging.spi.Logger defines it
 206         public final Level platformLevel; // The platform Level it will be mapped to in Jul when Jul is the backend
 207         public final String jdkExtensionToJUL; // The name of the method called on the JUL logger when JUL is the backend
 208         public final String julToJdkExtension; // The name of the method called in the jdk extension by the default impl in jdk.internal.logging.Logger
 209         public final String enableMethod; // The name of the isXxxxEnabled method
 210         public final boolean hasSpecificIsEnabled;
 211         Levels(String method, Class<?> definingClass, Level defaultMapping,
 212                 String jdkExtensionToJUL, String julToJdkExtension,
 213                 boolean hasSpecificIsEnabled) {
 214             this.method = method;
 215             this.definingClass = definingClass;
 216             this.platformLevel = defaultMapping;
 217             this.jdkExtensionToJUL = jdkExtensionToJUL;
 218             this.julToJdkExtension = julToJdkExtension;
 219             this.hasSpecificIsEnabled = hasSpecificIsEnabled;
 220             if (hasSpecificIsEnabled) {
 221                 this.enableMethod = "is" + method.substring(0,1).toUpperCase()
 222                     + method.substring(1) + "Enabled";
 223             } else {
 224                 this.enableMethod = "isLoggable";
 225             }
 226         }
 227 
 228         /*
 229          * calls this level specific method - e.g. if this==INFO: logger.info(msg);
 230          */
 231         public void level(Object logger, String msg) {
 232             MethodType mt = MethodType.methodType(void.class, Level.class, String.class);
 233             invoke("log", logger, mt, platformLevel, msg);
 234         }
 235 
 236         /*
 237          * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier);
 238          */
 239         public void level(Object logger, Supplier<String> msgSupplier) {
 240             MethodType mt = MethodType.methodType(void.class,  Level.class, Supplier.class);
 241             invoke("log", logger, mt, platformLevel, msgSupplier);
 242         }
 243 
 244         /*
 245          * calls this level specific method - e.g. if this==INFO: logger.info(msg, params);
 246          */
 247         public void level(Object logger, String msg, Object... params) {
 248             MethodType mt = MethodType.methodType(void.class,  Level.class, String.class,
 249                     Object[].class);
 250             invoke("log", logger, mt, platformLevel, msg, params);
 251         }
 252 
 253         /*
 254          * calls this level specific method - e.g. if this==INFO: logger.info(msg, thrown);
 255          */
 256         public void level(Object logger, String msg, Throwable thrown) {
 257             MethodType mt = MethodType.methodType(void.class,  Level.class, String.class,
 258                     Throwable.class);
 259             invoke("log", logger, mt, platformLevel, msg, thrown);
 260         }
 261 
 262         /*
 263          * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier, thrown);
 264          */
 265         public void level(Object logger, Supplier<String> msgSupplier, Throwable thrown) {
 266             MethodType mt = MethodType.methodType(void.class,  Level.class,
 267                      Throwable.class, Supplier.class);
 268             invoke("log", logger, mt, platformLevel, thrown, msgSupplier);
 269         }
 270 
 271         /*
 272          * calls this level specific method - e.g. if this==INFO: logger.info(bundle, msg);
 273          */
 274         public void level(Object logger, String msg, ResourceBundle bundle) {
 275             MethodType mt = MethodType.methodType(void.class, Level.class,
 276                     ResourceBundle.class, String.class, Object[].class);
 277             invoke("logrb", logger, mt, platformLevel, bundle, msg, null);
 278         }
 279 
 280         public void level(Object logger, String msg, ResourceBundle bundle,
 281                 Object... params) {
 282             MethodType mt = MethodType.methodType(void.class, Level.class,
 283                     ResourceBundle.class, String.class, Object[].class);
 284             invoke("logrb", logger, mt, platformLevel, bundle, msg, params);
 285         }
 286 
 287         public void level(Object logger, String msg, ResourceBundle bundle,
 288                 Throwable thrown) {
 289             MethodType mt = MethodType.methodType(void.class, Level.class,
 290                     ResourceBundle.class, String.class, Throwable.class);
 291             invoke("logrb", logger, mt, platformLevel, bundle, msg, thrown);
 292         }
 293 
 294         public boolean isEnabled(Object logger) {
 295             try {
 296                 if (hasSpecificIsEnabled) {
 297                     MethodType mt = MethodType.methodType(boolean.class);
 298                     final MethodHandle handle = lookup.findVirtual(definingClass,
 299                         enableMethod, mt).bindTo(logger);
 300                     return Boolean.class.cast(handle.invoke());
 301                 } else {
 302                     MethodType mt = MethodType.methodType(boolean.class,
 303                         Level.class);
 304                     final MethodHandle handle = lookup.findVirtual(definingClass,
 305                         enableMethod, mt).bindTo(logger);
 306                     return Boolean.class.cast(handle.invoke(platformLevel));
 307                 }
 308             } catch (Throwable ex) {
 309                 throw new RuntimeException(ex);
 310             }
 311         }
 312 
 313         private void invoke(String method, Object logger, MethodType mt, Object... args) {
 314             try {
 315                 final int last = mt.parameterCount()-1;
 316                 boolean isVarargs = mt.parameterType(last).isArray();
 317                 final MethodHandle handle = lookup.findVirtual(definingClass,
 318                         method, mt).bindTo(logger);
 319 
 320                 final StringBuilder builder = new StringBuilder();
 321                 builder.append(logger.getClass().getSimpleName()).append('.')
 322                         .append(method).append('(');
 323                 String sep = "";
 324                 int offset = 0;
 325                 Object[] params = args;
 326                 for (int i=0; (i-offset) < params.length; i++) {
 327                     if (isVarargs && i == last) {
 328                         offset = last;
 329                         params = (Object[])args[i];
 330                         if (params == null) break;
 331                     }
 332                     Object p = params[i - offset];
 333                     String quote = (p instanceof String) ? "\"" : "";
 334                     builder.append(sep).append(quote).append(p).append(quote);
 335                     sep = ", ";
 336                 }
 337                 builder.append(')');
 338                 if (verbose) {
 339                     System.out.println(builder);
 340                 }
 341                 handle.invokeWithArguments(args);
 342             } catch (Throwable ex) {
 343                 throw new RuntimeException(ex);
 344             }
 345         }
 346 
 347     };
 348 
 349     static interface Checker<LogResult, L> extends BiFunction<LogResult, L, Void> {}
 350     static interface JdkLogTester
 351             extends BiFunction<sun.util.logging.PlatformLogger.Bridge, Level, Void> {}
 352     static interface SpiLogTester
 353             extends BiFunction<java.lang.System.Logger, java.lang.System.Logger.Level, Void> {}
 354 
 355     static interface MethodInvoker<LOGGER, LEVEL> {
 356         public void logX(LOGGER logger, LEVEL level, Object... args);
 357     }
 358 
 359     public enum JdkLogMethodInvoker
 360            implements MethodInvoker<sun.util.logging.PlatformLogger.Bridge, Level> {
 361         /**
 362          * Tests {@link
 363          * jdk.internal.logging.Logger#log(Level, String, Object...)};
 364          **/
 365         LOG_STRING_PARAMS("log", MethodType.methodType(void.class,
 366                 Level.class, String.class, Object[].class)),
 367         /**
 368          * Tests {@link
 369          * jdk.internal.logging.Logger#log(Level, String, Throwable)};
 370          **/
 371         LOG_STRING_THROWN("log", MethodType.methodType(void.class,
 372                 Level.class, String.class, Throwable.class)),
 373         /**
 374          * Tests {@link
 375          * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
 376          **/
 377         LOG_SUPPLIER("log", MethodType.methodType(void.class,
 378                 Level.class, Supplier.class)),
 379         /**
 380          * Tests {@link
 381          * jdk.internal.logging.Logger#log(Level, Throwable, Supplier<String>)};
 382          **/
 383         LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class,
 384                 Level.class, Throwable.class, Supplier.class)),
 385         /**
 386          * Tests {@link
 387          * jdk.internal.logging.Logger#logp(Level, String, String, String)};
 388          **/
 389         LOGP_STRING("logp", MethodType.methodType(void.class,
 390                 Level.class, String.class, String.class, String.class)),
 391         /**
 392          * Tests {@link
 393          * jdk.internal.logging.Logger#logp(Level, String, String, String, Object...)};
 394          **/
 395         LOGP_STRING_PARAMS("logp", MethodType.methodType(void.class,
 396                 Level.class, String.class, String.class, String.class, Object[].class)),
 397         /**
 398          * Tests {@link
 399          * jdk.internal.logging.Logger#logp(Level, String, String, String, Throwable)};
 400          **/
 401         LOGP_STRING_THROWN("logp", MethodType.methodType(void.class,
 402                 Level.class, String.class, String.class, String.class, Throwable.class)),
 403         /**
 404          * Tests {@link
 405          * jdk.internal.logging.Logger#logp(Level, String, String, Supplier<String>)};
 406          **/
 407         LOGP_SUPPLIER("logp", MethodType.methodType(void.class,
 408                 Level.class, String.class, String.class, Supplier.class)),
 409         /**
 410          * Tests {@link
 411          * jdk.internal.logging.Logger#logp(Level, String, String, Throwable, Supplier<String>)};
 412          **/
 413         LOGP_SUPPLIER_THROWN("logp", MethodType.methodType(void.class,
 414                 Level.class, String.class, String.class,
 415                 Throwable.class, Supplier.class)),
 416         /**
 417          * Tests {@link
 418          * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)};
 419          **/
 420         LOGRB_STRING_PARAMS("logrb", MethodType.methodType(void.class,
 421                 Level.class, ResourceBundle.class, String.class, Object[].class)),
 422         /**
 423          * Tests {@link
 424          * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)};
 425          **/
 426         LOGRB_STRING_THROWN("logrb", MethodType.methodType(void.class,
 427                 Level.class, ResourceBundle.class, String.class, Throwable.class)),
 428         /**
 429          * Tests {@link
 430          * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Object...)};
 431          **/
 432         LOGRBP_STRING_PARAMS("logrb", MethodType.methodType(void.class,
 433                 Level.class, String.class, String.class, ResourceBundle.class,
 434                 String.class, Object[].class)),
 435         /**
 436          * Tests {@link
 437          * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Throwable)};
 438          **/
 439         LOGRBP_STRING_THROWN("logrb", MethodType.methodType(void.class,
 440                 Level.class, String.class, String.class, ResourceBundle.class,
 441                 String.class, Throwable.class)),
 442         ;
 443         final MethodType mt;
 444         final String method;
 445         JdkLogMethodInvoker(String method, MethodType mt) {
 446             this.mt = mt;
 447             this.method = method;
 448         }
 449         Object[] makeArgs(Level level, Object... rest) {
 450             List<Object> list = new ArrayList<>(rest == null ? 1 : rest.length + 1);
 451             list.add(level);
 452             if (rest != null) {
 453                 list.addAll(Arrays.asList(rest));
 454             }
 455             return list.toArray(new Object[list.size()]);
 456         }
 457 
 458         @Override
 459         public void logX(sun.util.logging.PlatformLogger.Bridge logger, Level level, Object... args) {
 460             try {
 461                 MethodHandle handle = lookup.findVirtual(bridgeLoggerClass,
 462                         method, mt).bindTo(logger);
 463                 final int last = mt.parameterCount()-1;
 464                 boolean isVarargs = mt.parameterType(last).isArray();
 465 
 466                 args = makeArgs(level, args);
 467 
 468                 final StringBuilder builder = new StringBuilder();
 469                 builder.append(logger.getClass().getSimpleName()).append('.')
 470                         .append(this.method).append('(');
 471                 String sep = "";
 472                 int offset = 0;
 473                 Object[] params = args;
 474                 for (int i=0; (i-offset) < params.length; i++) {
 475                     if (isVarargs && i == last) {
 476                         offset = last;
 477                         params = (Object[])args[i];
 478                         if (params == null) break;
 479                     }
 480                     Object p = params[i - offset];
 481                     String quote = (p instanceof String) ? "\"" : "";
 482                     p = p instanceof Level ? "Level."+p : p;
 483                     builder.append(sep).append(quote).append(p).append(quote);
 484                     sep = ", ";
 485                 }
 486                 builder.append(')');
 487                 if (verbose) System.out.println(builder);
 488                 handle.invokeWithArguments(args);
 489             } catch (Throwable ex) {
 490                 throw new RuntimeException(ex);
 491             }
 492         }
 493     }
 494 
 495 
 496     public enum SpiLogMethodInvoker implements MethodInvoker<java.lang.System.Logger,
 497             java.lang.System.Logger.Level> {
 498         /**
 499          * Tests {@link
 500          * jdk.internal.logging.Logger#log(Level, String, Object...)};
 501          **/
 502         LOG_STRING_PARAMS("log", MethodType.methodType(void.class,
 503                 java.lang.System.Logger.Level.class, String.class, Object[].class)),
 504         /**
 505          * Tests {@link
 506          * jdk.internal.logging.Logger#log(Level, String, Throwable)};
 507          **/
 508         LOG_STRING_THROWN("log", MethodType.methodType(void.class,
 509                 java.lang.System.Logger.Level.class, String.class, Throwable.class)),
 510         /**
 511          * Tests {@link
 512          * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
 513          **/
 514         LOG_SUPPLIER("log", MethodType.methodType(void.class,
 515                 java.lang.System.Logger.Level.class, Supplier.class)),
 516         /**
 517          * Tests {@link
 518          * jdk.internal.logging.Logger#log(Level, Throwable, Supplier<String>)};
 519          **/
 520         LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class,
 521                 java.lang.System.Logger.Level.class, Supplier.class, Throwable.class)),
 522         /**
 523          * Tests {@link
 524          * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
 525          **/
 526         LOG_OBJECT("log", MethodType.methodType(void.class,
 527                 java.lang.System.Logger.Level.class, Object.class)),
 528         /**
 529          * Tests {@link
 530          * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)};
 531          **/
 532         LOGRB_STRING_PARAMS("log", MethodType.methodType(void.class,
 533                 java.lang.System.Logger.Level.class, ResourceBundle.class,
 534                 String.class, Object[].class)),
 535         /**
 536          * Tests {@link
 537          * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)};
 538          **/
 539         LOGRB_STRING_THROWN("log", MethodType.methodType(void.class,
 540                 java.lang.System.Logger.Level.class, ResourceBundle.class,
 541                 String.class, Throwable.class)),
 542         ;
 543         final MethodType mt;
 544         final String method;
 545         SpiLogMethodInvoker(String method, MethodType mt) {
 546             this.mt = mt;
 547             this.method = method;
 548         }
 549         Object[] makeArgs(java.lang.System.Logger.Level level, Object... rest) {
 550             List<Object> list = new ArrayList<>(rest == null ? 1 : rest.length + 1);
 551             list.add(level);
 552             if (rest != null) {
 553                 list.addAll(Arrays.asList(rest));
 554             }
 555             return list.toArray(new Object[list.size()]);
 556         }
 557 
 558         @Override
 559         public void logX(java.lang.System.Logger logger,
 560                 java.lang.System.Logger.Level level, Object... args) {
 561             try {
 562                 MethodHandle handle = lookup.findVirtual(spiLoggerClass,
 563                         method, mt).bindTo(logger);
 564                 final int last = mt.parameterCount()-1;
 565                 boolean isVarargs = mt.parameterType(last).isArray();
 566 
 567                 args = makeArgs(level, args);
 568 
 569                 final StringBuilder builder = new StringBuilder();
 570                 builder.append(logger.getClass().getSimpleName()).append('.')
 571                         .append(this.method).append('(');
 572                 String sep = "";
 573                 int offset = 0;
 574                 Object[] params = args;
 575                 for (int i=0; (i-offset) < params.length; i++) {
 576                     if (isVarargs && i == last) {
 577                         offset = last;
 578                         params = (Object[])args[i];
 579                         if (params == null) break;
 580                     }
 581                     Object p = params[i - offset];
 582                     String quote = (p instanceof String) ? "\"" : "";
 583                     p = p instanceof Level ? "Level."+p : p;
 584                     builder.append(sep).append(quote).append(p).append(quote);
 585                     sep = ", ";
 586                 }
 587                 builder.append(')');
 588                 if (verbose) System.out.println(builder);
 589                 handle.invokeWithArguments(args);
 590             } catch (Throwable ex) {
 591                 throw new RuntimeException(ex);
 592             }
 593         }
 594     }
 595 
 596 
 597     public abstract static class BackendTester<BackendRecord> {
 598         static final Level[] levelMap = {Level.ALL, Level.FINER, Level.FINE,
 599             Level.INFO, Level.WARNING, Level.SEVERE, Level.OFF};
 600 
 601         abstract class BackendAdaptor {
 602             public abstract String getLoggerName(BackendRecord res);
 603             public abstract Object getLevel(BackendRecord res);
 604             public abstract String getMessage(BackendRecord res);
 605             public abstract String getSourceClassName(BackendRecord res);
 606             public abstract String getSourceMethodName(BackendRecord res);
 607             public abstract Throwable getThrown(BackendRecord res);
 608             public abstract ResourceBundle getResourceBundle(BackendRecord res);
 609             public abstract void setLevel(java.lang.System.Logger logger,
 610                     Level level);
 611             public abstract void setLevel(java.lang.System.Logger logger,
 612                     java.lang.System.Logger.Level level);
 613             public abstract List<BackendRecord> getBackendRecords();
 614             public abstract void resetBackendRecords();
 615             public boolean shouldBeLoggable(Levels level, Level loggerLevel) {
 616                 final Level logLevel = level.platformLevel;
 617                 return shouldBeLoggable(logLevel, loggerLevel);
 618             }
 619             public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) {
 620                 return loggerLevel.intValue() != Level.OFF.intValue()
 621                         && logLevel.intValue() >= loggerLevel.intValue();
 622             }
 623             public boolean shouldBeLoggable(java.lang.System.Logger.Level logLevel,
 624                     java.lang.System.Logger.Level loggerLevel) {
 625                 return loggerLevel != java.lang.System.Logger.Level.OFF
 626                         && logLevel.ordinal() >= loggerLevel.ordinal();
 627             }
 628             public boolean isLoggable(java.lang.System.Logger logger, Level l) {
 629                 return bridgeLoggerClass.cast(logger).isLoggable(l);
 630             }
 631             public String getCallerClassName(Levels level, String clazz) {
 632                 return clazz != null ? clazz : Levels.class.getName();
 633             }
 634             public String getCallerClassName(MethodInvoker<?,?> logMethod,
 635                    String clazz) {
 636                 return clazz != null ? clazz : logMethod.getClass().getName();
 637             }
 638             public String getCallerMethodName(Levels level, String method) {
 639                 return method != null ? method : "invoke";
 640             }
 641             public String getCallerMethodName(MethodInvoker<?,?> logMethod,
 642                     String method) {
 643                 return method != null ? method : "logX";
 644             }
 645             public Object getMappedLevel(Object level) {
 646                 return level;
 647             }
 648 
 649             public Level toJUL(java.lang.System.Logger.Level level) {
 650                 return levelMap[level.ordinal()];
 651             }
 652         }
 653 
 654         public final boolean isSystem;
 655         public final Class<? extends java.lang.System.Logger> restrictedTo;
 656         public final ResourceBundle localized;
 657         public BackendTester(boolean isSystem) {
 658             this(isSystem,null,null);
 659         }
 660         public BackendTester(boolean isSystem, ResourceBundle localized) {
 661             this(isSystem,null,localized);
 662         }
 663         public BackendTester(boolean isSystem,
 664                 Class<? extends java.lang.System.Logger> restrictedTo) {
 665             this(isSystem, restrictedTo, null);
 666         }
 667         public BackendTester(boolean isSystem,
 668                 Class<? extends java.lang.System.Logger> restrictedTo,
 669                 ResourceBundle localized) {
 670             this.isSystem = isSystem;
 671             this.restrictedTo = restrictedTo;
 672             this.localized = localized;
 673         }
 674 
 675         public java.lang.System.Logger convert(java.lang.System.Logger logger) {
 676             return logger;
 677         }
 678 
 679         public static Level[] LEVELS = {
 680             Level.OFF,
 681             Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG,
 682             Level.FINE, Level.FINER, Level.FINEST,
 683             Level.ALL
 684         };
 685 
 686         abstract BackendAdaptor adaptor();
 687 
 688         protected void checkRecord(Levels test, BackendRecord res, String loggerName,
 689                 Level level, String msg, String className, String methodName,
 690                 Throwable thrown, ResourceBundle bundle, Object... params) {
 691             checkRecord(test, res, loggerName, level, ()->msg, className,
 692                     methodName, thrown, bundle, params);
 693 
 694         }
 695         protected void checkRecord(Levels test, BackendRecord res, String loggerName,
 696                 Level level, Supplier<String> msg, String className, String methodName,
 697                 Throwable thrown, ResourceBundle bundle, Object... params) {
 698             checkRecord(test.method, res, loggerName, level, msg,
 699                     className, methodName, thrown, bundle, params);
 700         }
 701         protected <L> void checkRecord(String logMethod, BackendRecord res, String loggerName,
 702                 L level, Supplier<String> msg, String className, String methodName,
 703                 Throwable thrown, ResourceBundle bundle, Object... params) {
 704             final BackendAdaptor analyzer = adaptor();
 705             if (! Objects.equals(analyzer.getLoggerName(res), loggerName)) {
 706                 throw new RuntimeException(logMethod+": expected logger name "
 707                         + loggerName + " got " + analyzer.getLoggerName(res));
 708             }
 709             if (!Objects.equals(analyzer.getLevel(res), analyzer.getMappedLevel(level))) {
 710                 throw new RuntimeException(logMethod+": expected level "
 711                         + analyzer.getMappedLevel(level) + " got " + analyzer.getLevel(res));
 712             }
 713             if (!Objects.equals(analyzer.getMessage(res), msg.get())) {
 714                 throw new RuntimeException(logMethod+": expected message \""
 715                         + msg.get() + "\" got \"" + analyzer.getMessage(res) +"\"");
 716             }
 717             if (!Objects.equals(analyzer.getSourceClassName(res), className)) {
 718                 throw new RuntimeException(logMethod
 719                         + ": expected class name \"" + className
 720                         + "\" got \"" + analyzer.getSourceClassName(res) +"\"");
 721             }
 722             if (!Objects.equals(analyzer.getSourceMethodName(res), methodName)) {
 723                 throw new RuntimeException(logMethod
 724                         + ": expected method name \"" + methodName
 725                         + "\" got \"" + analyzer.getSourceMethodName(res) +"\"");
 726             }
 727             final Throwable thrownRes = analyzer.getThrown(res);
 728             if (!Objects.equals(thrownRes, thrown)) {
 729                 throw new RuntimeException(logMethod
 730                         + ": expected throwable \"" + thrown
 731                         + "\" got \"" + thrownRes + "\"");
 732             }
 733             if (!Objects.equals(analyzer.getResourceBundle(res), bundle)) {
 734                 throw new RuntimeException(logMethod
 735                         + ": expected bundle \"" + bundle
 736                         + "\" got \"" + analyzer.getResourceBundle(res) +"\"");
 737             }
 738         }
 739 
 740         public void testLevel(Levels level, java.lang.System.Logger logger,
 741                 String msg) {
 742             Runnable test = () -> level.level(logger, msg);
 743             Checker<BackendRecord, Level> check = (res, l) -> {
 744                 checkRecord(level, res, logger.getName(), l, msg,
 745                             adaptor().getCallerClassName(level, Levels.class.getName()),
 746                             adaptor().getCallerMethodName(level, "invoke"),
 747                             null, localized);
 748                 return null;
 749             };
 750             test("msg", level, logger, test, check);
 751         }
 752 
 753         public void testLevel(Levels level, java.lang.System.Logger logger,
 754                 String msg, Object... params) {
 755             Runnable test = () -> level.level(logger, msg, (Object[])params);
 756             Checker<BackendRecord, Level> check = (res, l) -> {
 757                 checkRecord(level, res, logger.getName(), l, msg,
 758                             adaptor().getCallerClassName(level, Levels.class.getName()),
 759                             adaptor().getCallerMethodName(level, "invoke"),
 760                             null, localized, (Object[])params);
 761                 return null;
 762             };
 763             test("msg, params", level, logger, test, check);
 764         }
 765 
 766         public void testLevel(Levels level, java.lang.System.Logger logger,
 767                 String msg, Throwable thrown) {
 768             Runnable test = () -> level.level(logger, msg, thrown);
 769             Checker<BackendRecord, Level> check = (res, l) -> {
 770                 checkRecord(level, res, logger.getName(), l, msg,
 771                             adaptor().getCallerClassName(level, Levels.class.getName()),
 772                             adaptor().getCallerMethodName(level, "invoke"),
 773                             thrown, localized);
 774                 return null;
 775             };
 776             test("msg, thrown", level, logger, test, check);
 777         }
 778 
 779         public void testLevel(Levels level, java.lang.System.Logger logger,
 780                 Supplier<String> msg) {
 781             Runnable test = () -> level.level(logger, msg);
 782             Checker<BackendRecord, Level> check = (res, l) -> {
 783                 checkRecord(level, res, logger.getName(), l, msg,
 784                             adaptor().getCallerClassName(level, Levels.class.getName()),
 785                             adaptor().getCallerMethodName(level, "invoke"),
 786                             null, null);
 787                 return null;
 788             };
 789             test("msgSupplier", level, logger, test, check);
 790         }
 791 
 792         public void testLevel(Levels level, java.lang.System.Logger logger,
 793                 Supplier<String> msg, Throwable thrown) {
 794             Runnable test = () -> level.level(logger, msg, thrown);
 795             Checker<BackendRecord, Level> check = (res, l) -> {
 796                 checkRecord(level, res, logger.getName(), l, msg,
 797                             adaptor().getCallerClassName(level, Levels.class.getName()),
 798                             adaptor().getCallerMethodName(level, "invoke"),
 799                             thrown, null);
 800                 return null;
 801             };
 802             test("throw, msgSupplier", level, logger, test, check);
 803         }
 804 
 805         public void testLevel(Levels level, java.lang.System.Logger logger,
 806                 String msg, ResourceBundle bundle) {
 807             Runnable test = () -> level.level(logger, msg, bundle);
 808             Checker<BackendRecord, Level> check = (res, l) -> {
 809                 checkRecord(level, res, logger.getName(), l, msg,
 810                             adaptor().getCallerClassName(level, Levels.class.getName()),
 811                             adaptor().getCallerMethodName(level, "invoke"),
 812                             null, bundle);
 813                 return null;
 814             };
 815             test("bundle, msg", level, logger, test, check);
 816         }
 817 
 818         public void testLevel(Levels level, java.lang.System.Logger logger,
 819                 String msg, ResourceBundle bundle, Object... params) {
 820             Runnable test = () -> level.level(logger, msg, bundle, (Object[])params);
 821             Checker<BackendRecord, Level> check = (res, l) -> {
 822                 checkRecord(level, res, logger.getName(), l, msg,
 823                             adaptor().getCallerClassName(level, Levels.class.getName()),
 824                             adaptor().getCallerMethodName(level, "invoke"),
 825                             null, bundle, (Object[])params);
 826                 return null;
 827             };
 828             test("bundle, msg, params", level, logger, test, check);
 829         }
 830 
 831         public void testLevel(Levels level, java.lang.System.Logger logger,
 832                 String msg, ResourceBundle bundle, Throwable thrown) {
 833             Runnable test = () -> level.level(logger, msg, bundle, thrown);
 834             Checker<BackendRecord, Level> check = (res, l) -> {
 835                 checkRecord(level, res, logger.getName(), l, msg,
 836                             adaptor().getCallerClassName(level, Levels.class.getName()),
 837                             adaptor().getCallerMethodName(level, "invoke"),
 838                             thrown, bundle);
 839                 return null;
 840             };
 841             test("bundle, msg, throwable", level, logger, test, check);
 842         }
 843 
 844         // System.Logger
 845         public void testSpiLog(java.lang.System.Logger logger, String msg) {
 846             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 847                 checkRecord("log", res, logger.getName(), l, () -> msg,
 848                             adaptor().getCallerClassName(
 849                                     SpiLogMethodInvoker.LOG_STRING_PARAMS,
 850                                     SpiLogMethodInvoker.class.getName()),
 851                             adaptor().getCallerMethodName(
 852                                     SpiLogMethodInvoker.LOG_STRING_PARAMS,
 853                                     "logX"), null, localized);
 854                 return null;
 855             };
 856             SpiLogTester tester = (x, level) -> {
 857                 SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null);
 858                 return null;
 859             };
 860             Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")";
 861             testSpiLog(logger, tester, check, nameProducer);
 862         }
 863 
 864         public void testSpiLog(java.lang.System.Logger logger,
 865                 ResourceBundle bundle, String msg) {
 866             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 867                 checkRecord("log", res, logger.getName(), l, () -> msg,
 868                             adaptor().getCallerClassName(
 869                                     SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
 870                                     SpiLogMethodInvoker.class.getName()),
 871                             adaptor().getCallerMethodName(
 872                                     SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
 873                                     "logX"), null, bundle);
 874                 return null;
 875             };
 876             SpiLogTester tester = (x, level) -> {
 877                 SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null);
 878                 return null;
 879             };
 880             Function<String, String> nameProducer = (l) -> "log(Level." + l
 881                     + ", bundle, \"" + msg + "\")";
 882             testSpiLog(logger, tester, check, nameProducer);
 883         }
 884 
 885         public void testSpiLog(java.lang.System.Logger logger, String msg, Object... params) {
 886             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 887                 checkRecord("log", res, logger.getName(), l, () -> msg,
 888                             adaptor().getCallerClassName(
 889                                     SpiLogMethodInvoker.LOG_STRING_PARAMS,
 890                                     SpiLogMethodInvoker.class.getName()),
 891                             adaptor().getCallerMethodName(
 892                                     SpiLogMethodInvoker.LOG_STRING_PARAMS,
 893                                     "logX"), null, localized, params);
 894                 return null;
 895             };
 896             SpiLogTester tester = (x, level) -> {
 897                 SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params);
 898                 return null;
 899             };
 900             Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)";
 901             testSpiLog(logger, tester, check, nameProducer);
 902         }
 903 
 904         public void testSpiLog(java.lang.System.Logger logger,
 905                 ResourceBundle bundle, String msg, Object... params) {
 906             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 907                 checkRecord("log", res, logger.getName(), l, () -> msg,
 908                             adaptor().getCallerClassName(
 909                                     SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
 910                                     SpiLogMethodInvoker.class.getName()),
 911                             adaptor().getCallerMethodName(
 912                                     SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
 913                                     "logX"), null, bundle, params);
 914                 return null;
 915             };
 916             SpiLogTester tester = (x, level) -> {
 917                 SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params);
 918                 return null;
 919             };
 920             Function<String, String> nameProducer = (l) -> "log(Level." + l
 921                     + ", bundle, \"" + msg + "\", params...)";
 922             testSpiLog(logger, tester, check, nameProducer);
 923         }
 924 
 925         public void testSpiLog(java.lang.System.Logger logger, String msg, Throwable thrown) {
 926             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 927                 checkRecord("log", res, logger.getName(), l, () -> msg,
 928                            adaptor().getCallerClassName(
 929                                     SpiLogMethodInvoker.LOG_STRING_THROWN,
 930                                     SpiLogMethodInvoker.class.getName()),
 931                             adaptor().getCallerMethodName(
 932                                     SpiLogMethodInvoker.LOG_STRING_THROWN,
 933                                     "logX"), thrown, localized);
 934                 return null;
 935             };
 936             SpiLogTester tester = (x, level) -> {
 937                 SpiLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown);
 938                 return null;
 939             };
 940             Function<String, String> nameProducer = (l) ->
 941                     "log(Level." + l + ", \"" + msg + "\", thrown)";
 942             testSpiLog(logger, tester, check, nameProducer);
 943         }
 944 
 945         public void testSpiLog(java.lang.System.Logger logger,
 946                 ResourceBundle bundle, String msg, Throwable thrown) {
 947             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 948                 checkRecord("log", res, logger.getName(), l, () -> msg,
 949                             adaptor().getCallerClassName(
 950                                     SpiLogMethodInvoker.LOGRB_STRING_THROWN,
 951                                     SpiLogMethodInvoker.class.getName()),
 952                             adaptor().getCallerMethodName(
 953                                     SpiLogMethodInvoker.LOGRB_STRING_THROWN,
 954                                     "logX"), thrown, bundle);
 955                 return null;
 956             };
 957             SpiLogTester tester = (x, level) -> {
 958                 SpiLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown);
 959                 return null;
 960             };
 961             Function<String, String> nameProducer = (l) ->
 962                     "log(Level." + l + ", bundle, \"" + msg + "\", thrown)";
 963             testSpiLog(logger, tester, check, nameProducer);
 964         }
 965 
 966         public void testSpiLog(java.lang.System.Logger logger, Supplier<String> msg) {
 967             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 968                 checkRecord("log", res, logger.getName(), l, msg,
 969                             adaptor().getCallerClassName(
 970                                     SpiLogMethodInvoker.LOG_SUPPLIER,
 971                                     SpiLogMethodInvoker.class.getName()),
 972                             adaptor().getCallerMethodName(
 973                                     SpiLogMethodInvoker.LOG_SUPPLIER,
 974                                     "logX"), null, null);
 975                 return null;
 976             };
 977             SpiLogTester tester = (x, level) -> {
 978                 SpiLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg);
 979                 return null;
 980             };
 981             Function<String, String> nameProducer = (l) ->
 982                     "log(Level." + l + ", () -> \"" + msg.get() + "\")";
 983             testSpiLog(logger, tester, check, nameProducer);
 984         }
 985 
 986         public void testSpiLog(java.lang.System.Logger logger, Object obj) {
 987             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
 988                 checkRecord("log", res, logger.getName(), l, () -> obj.toString(),
 989                             adaptor().getCallerClassName(
 990                                     SpiLogMethodInvoker.LOG_OBJECT,
 991                                     SpiLogMethodInvoker.class.getName()),
 992                             adaptor().getCallerMethodName(
 993                                     SpiLogMethodInvoker.LOG_OBJECT,
 994                                     "logX"), null, null);
 995                 return null;
 996             };
 997             SpiLogTester tester = (x, level) -> {
 998                 SpiLogMethodInvoker.LOG_OBJECT.logX(x, level, obj);
 999                 return null;
1000             };
1001             Function<String, String> nameProducer = (l) ->
1002                     "log(Level." + l + ", new "+obj.getClass().getSimpleName()+"(\""
1003                             + obj.toString() + "\"))";
1004             testSpiLog(logger, tester, check, nameProducer);
1005         }
1006 
1007         public void testSpiLog(java.lang.System.Logger logger, Throwable thrown, Supplier<String> msg) {
1008             Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
1009                 checkRecord("log", res, logger.getName(), l, msg,
1010                             adaptor().getCallerClassName(
1011                                     SpiLogMethodInvoker.LOG_SUPPLIER_THROWN,
1012                                     SpiLogMethodInvoker.class.getName()),
1013                             adaptor().getCallerMethodName(
1014                                     SpiLogMethodInvoker.LOG_SUPPLIER_THROWN,
1015                                     "logX"), thrown, null);
1016                 return null;
1017             };
1018             SpiLogTester tester = (x, level) -> {
1019                 SpiLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, msg, thrown);
1020                 return null;
1021             };
1022             Function<String, String> nameProducer = (l) ->
1023                     "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)";
1024             testSpiLog(logger, tester, check, nameProducer);
1025         }
1026 
1027 
1028         // JDK
1029 
1030         public void testLog(java.lang.System.Logger logger, String msg) {
1031             Checker<BackendRecord, Level> check = (res, l) -> {
1032                 checkRecord("log", res, logger.getName(), l, () -> msg,
1033                             adaptor().getCallerClassName(
1034                                     JdkLogMethodInvoker.LOG_STRING_PARAMS,
1035                                     JdkLogMethodInvoker.class.getName()),
1036                             adaptor().getCallerMethodName(
1037                                     JdkLogMethodInvoker.LOG_STRING_PARAMS,
1038                                     "logX"), null, localized);
1039                 return null;
1040             };
1041             JdkLogTester tester = (x, level) -> {
1042                 JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null);
1043                 return null;
1044             };
1045             Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")";
1046             testJdkLog(logger, tester, check, nameProducer);
1047         }
1048 
1049         public void testLogrb(java.lang.System.Logger logger,
1050                 ResourceBundle bundle, String msg) {
1051             Checker<BackendRecord, Level> check = (res, l) -> {
1052                 checkRecord("log", res, logger.getName(), l, () -> msg,
1053                             adaptor().getCallerClassName(
1054                                     JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
1055                                     JdkLogMethodInvoker.class.getName()),
1056                             adaptor().getCallerMethodName(
1057                                     JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
1058                                     "logX"), null, bundle);
1059                 return null;
1060             };
1061             JdkLogTester tester = (x, level) -> {
1062                 JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null);
1063                 return null;
1064             };
1065             Function<String, String> nameProducer = (l) -> "logrb(Level." + l
1066                     + ", bundle, \"" + msg + "\")";
1067             testJdkLog(logger, tester, check, nameProducer);
1068         }
1069 
1070         public void testLog(java.lang.System.Logger logger, String msg, Object... params) {
1071             Checker<BackendRecord, Level> check = (res, l) -> {
1072                 checkRecord("log", res, logger.getName(), l, () -> msg,
1073                             adaptor().getCallerClassName(
1074                                     JdkLogMethodInvoker.LOG_STRING_PARAMS,
1075                                     JdkLogMethodInvoker.class.getName()),
1076                             adaptor().getCallerMethodName(
1077                                     JdkLogMethodInvoker.LOG_STRING_PARAMS,
1078                                     "logX"), null, localized, params);
1079                 return null;
1080             };
1081             JdkLogTester tester = (x, level) -> {
1082                 JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params);
1083                 return null;
1084             };
1085             Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)";
1086             testJdkLog(logger, tester, check, nameProducer);
1087         }
1088 
1089         public void testLogrb(java.lang.System.Logger logger,
1090                 ResourceBundle bundle, String msg, Object... params) {
1091             Checker<BackendRecord, Level> check = (res, l) -> {
1092                 checkRecord("log", res, logger.getName(), l, () -> msg,
1093                             adaptor().getCallerClassName(
1094                                     JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
1095                                     JdkLogMethodInvoker.class.getName()),
1096                             adaptor().getCallerMethodName(
1097                                     JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
1098                                     "logX"), null, bundle, params);
1099                 return null;
1100             };
1101             JdkLogTester tester = (x, level) -> {
1102                 JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params);
1103                 return null;
1104             };
1105             Function<String, String> nameProducer = (l) -> "log(Level." + l
1106                     + ", bundle, \"" + msg + "\", params...)";
1107             testJdkLog(logger, tester, check, nameProducer);
1108         }
1109 
1110         public void testLog(java.lang.System.Logger logger, String msg, Throwable thrown) {
1111             Checker<BackendRecord, Level> check = (res, l) -> {
1112                 checkRecord("log", res, logger.getName(), l, () -> msg,
1113                            adaptor().getCallerClassName(
1114                                     JdkLogMethodInvoker.LOG_STRING_THROWN,
1115                                     JdkLogMethodInvoker.class.getName()),
1116                             adaptor().getCallerMethodName(
1117                                     JdkLogMethodInvoker.LOG_STRING_THROWN,
1118                                     "logX"), thrown, localized);
1119                 return null;
1120             };
1121             JdkLogTester tester = (x, level) -> {
1122                 JdkLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown);
1123                 return null;
1124             };
1125             Function<String, String> nameProducer = (l) ->
1126                     "log(Level." + l + ", \"" + msg + "\", thrown)";
1127             testJdkLog(logger, tester, check, nameProducer);
1128         }
1129 
1130         public void testLogrb(java.lang.System.Logger logger,
1131                 ResourceBundle bundle, String msg, Throwable thrown) {
1132             Checker<BackendRecord, Level> check = (res, l) -> {
1133                 checkRecord("log", res, logger.getName(), l, () -> msg,
1134                             adaptor().getCallerClassName(
1135                                     JdkLogMethodInvoker.LOGRB_STRING_THROWN,
1136                                     JdkLogMethodInvoker.class.getName()),
1137                             adaptor().getCallerMethodName(
1138                                     JdkLogMethodInvoker.LOGRB_STRING_THROWN,
1139                                     "logX"), thrown, bundle);
1140                 return null;
1141             };
1142             JdkLogTester tester = (x, level) -> {
1143                 JdkLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown);
1144                 return null;
1145             };
1146             Function<String, String> nameProducer = (l) ->
1147                     "log(Level." + l + ", bundle, \"" + msg + "\", thrown)";
1148             testJdkLog(logger, tester, check, nameProducer);
1149         }
1150 
1151         public void testLog(java.lang.System.Logger logger, Supplier<String> msg) {
1152             Checker<BackendRecord, Level> check = (res, l) -> {
1153                 checkRecord("log", res, logger.getName(), l, msg,
1154                             adaptor().getCallerClassName(
1155                                     JdkLogMethodInvoker.LOG_SUPPLIER,
1156                                     JdkLogMethodInvoker.class.getName()),
1157                             adaptor().getCallerMethodName(
1158                                     JdkLogMethodInvoker.LOG_SUPPLIER,
1159                                     "logX"), null, null);
1160                 return null;
1161             };
1162             JdkLogTester tester = (x, level) -> {
1163                 JdkLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg);
1164                 return null;
1165             };
1166             Function<String, String> nameProducer = (l) ->
1167                     "log(Level." + l + ", () -> \"" + msg.get() + "\")";
1168             testJdkLog(logger, tester, check, nameProducer);
1169         }
1170 
1171         public void testLog(java.lang.System.Logger logger, Throwable thrown, Supplier<String> msg) {
1172             Checker<BackendRecord, Level> check = (res, l) -> {
1173                 checkRecord("log", res, logger.getName(), l, msg,
1174                             adaptor().getCallerClassName(
1175                                     JdkLogMethodInvoker.LOG_SUPPLIER_THROWN,
1176                                     JdkLogMethodInvoker.class.getName()),
1177                             adaptor().getCallerMethodName(
1178                                     JdkLogMethodInvoker.LOG_SUPPLIER_THROWN,
1179                                     "logX"), thrown, null);
1180                 return null;
1181             };
1182             JdkLogTester tester = (x, level) -> {
1183                 JdkLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, thrown, msg);
1184                 return null;
1185             };
1186             Function<String, String> nameProducer = (l) ->
1187                     "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)";
1188             testJdkLog(logger, tester, check, nameProducer);
1189         }
1190 
1191         static Supplier<String> logpMessage(ResourceBundle bundle,
1192                 String className, String methodName, Supplier<String> msg) {
1193             if (BEST_EFFORT_FOR_LOGP && bundle == null
1194                     && (className != null || methodName != null)) {
1195                 final String cName = className == null ? "" :  className;
1196                 final String mName = methodName == null ? "" : methodName;
1197                 return () -> {
1198                     String m = msg.get();
1199                     return String.format("[%s %s] %s", cName, mName, m == null ? "" : m);
1200                 };
1201             } else {
1202                 return msg;
1203             }
1204         }
1205 
1206         public void testLogp(java.lang.System.Logger logger, String className,
1207                 String methodName, String msg) {
1208             Checker<BackendRecord, Level> check = (res, l) -> {
1209                 checkRecord("logp", res, logger.getName(), l,
1210                             logpMessage(localized, className, methodName, () -> msg),
1211                             adaptor().getCallerClassName(
1212                                     JdkLogMethodInvoker.LOGP_STRING, className),
1213                             adaptor().getCallerClassName(
1214                                     JdkLogMethodInvoker.LOGP_STRING, methodName),
1215                             null, localized);
1216                 return null;
1217             };
1218             JdkLogTester tester = (x, level) -> {
1219                 JdkLogMethodInvoker.LOGP_STRING.logX(x, level,
1220                         className, methodName, msg);
1221                 return null;
1222             };
1223             Function<String, String> nameProducer = (l) ->
1224                     "logp(Level." + l + ", class, method, \"" + msg + "\")";
1225             testJdkLog(logger, tester, check, nameProducer);
1226         }
1227 
1228         public void testLogrb(java.lang.System.Logger logger, String className,
1229                 String methodName, ResourceBundle bundle, String msg) {
1230             Checker<BackendRecord, Level> check = (res, l) -> {
1231                 checkRecord("logp", res, logger.getName(), l, () -> msg,
1232                             adaptor().getCallerClassName(
1233                                     JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className),
1234                             adaptor().getCallerClassName(
1235                                     JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName),
1236                             null, bundle);
1237                 return null;
1238             };
1239             JdkLogTester tester = (x, level) -> {
1240                 JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level,
1241                         className, methodName, bundle, msg, (Object[])null);
1242                 return null;
1243             };
1244             Function<String, String> nameProducer = (l) ->
1245                     "logp(Level." + l + ", class, method, bundle, \"" + msg + "\")";
1246             testJdkLog(logger, tester, check, nameProducer);
1247         }
1248 
1249         public void testLogp(java.lang.System.Logger logger, String className,
1250                 String methodName, String msg, Object... params) {
1251             Checker<BackendRecord, Level> check = (res, l) -> {
1252                 checkRecord("logp", res, logger.getName(), l,
1253                             logpMessage(localized, className, methodName, () -> msg),
1254                             adaptor().getCallerClassName(
1255                                     JdkLogMethodInvoker.LOGP_STRING_PARAMS, className),
1256                             adaptor().getCallerClassName(
1257                                     JdkLogMethodInvoker.LOGP_STRING_PARAMS, methodName),
1258                             null, localized, params);
1259                 return null;
1260             };
1261             JdkLogTester tester = (x, level) -> {
1262                 JdkLogMethodInvoker.LOGP_STRING_PARAMS.logX(x, level,
1263                         className, methodName, msg, params);
1264                 return null;
1265             };
1266             Function<String, String> nameProducer = (l) ->
1267                     "log(Level." + l + ", class, method, \"" + msg + "\", params...)";
1268             testJdkLog(logger, tester, check, nameProducer);
1269         }
1270 
1271         public void testLogrb(java.lang.System.Logger logger, String className,
1272                 String methodName, ResourceBundle bundle, String msg,
1273                 Object... params) {
1274             Checker<BackendRecord, Level> check = (res, l) -> {
1275                 checkRecord("logp", res, logger.getName(), l, () -> msg,
1276                             adaptor().getCallerClassName(
1277                                     JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className),
1278                             adaptor().getCallerClassName(
1279                                     JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName),
1280                             null, bundle, params);
1281                 return null;
1282             };
1283             JdkLogTester tester = (x, level) -> {
1284                 JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level,
1285                         className, methodName, bundle, msg, params);
1286                 return null;
1287             };
1288             Function<String, String> nameProducer = (l) ->
1289                     "log(Level." + l + ", class, method, bundle, \""
1290                             + msg + "\", params...)";
1291             testJdkLog(logger, tester, check, nameProducer);
1292         }
1293 
1294         public void testLogp(java.lang.System.Logger logger, String className,
1295                 String methodName, String msg, Throwable thrown) {
1296             Checker<BackendRecord, Level> check = (res, l) -> {
1297                 checkRecord("log", res, logger.getName(), l,
1298                             logpMessage(localized, className, methodName, () -> msg),
1299                             adaptor().getCallerClassName(
1300                                     JdkLogMethodInvoker.LOGP_STRING_THROWN, className),
1301                             adaptor().getCallerClassName(
1302                                     JdkLogMethodInvoker.LOGP_STRING_THROWN, methodName),
1303                             thrown, localized);
1304                 return null;
1305             };
1306             JdkLogTester tester = (x, level) -> {
1307                 JdkLogMethodInvoker.LOGP_STRING_THROWN.logX(x, level,
1308                         className, methodName, msg, thrown);
1309                 return null;
1310             };
1311             Function<String, String> nameProducer = (l) ->
1312                     "log(Level." + l + ", class, method, \"" + msg + "\", thrown)";
1313             testJdkLog(logger, tester, check, nameProducer);
1314         }
1315 
1316         public void testLogrb(java.lang.System.Logger logger, String className,
1317                 String methodName, ResourceBundle bundle,
1318                 String msg, Throwable thrown) {
1319             Checker<BackendRecord, Level> check = (res, l) -> {
1320                 checkRecord("log", res, logger.getName(), l, () -> msg,
1321                             adaptor().getCallerClassName(
1322                                     JdkLogMethodInvoker.LOGRBP_STRING_THROWN, className),
1323                             adaptor().getCallerClassName(
1324                                     JdkLogMethodInvoker.LOGRBP_STRING_THROWN, methodName),
1325                             thrown, bundle);
1326                 return null;
1327             };
1328             JdkLogTester tester = (x, level) -> {
1329                 JdkLogMethodInvoker.LOGRBP_STRING_THROWN.logX(x, level,
1330                         className, methodName, bundle, msg, thrown);
1331                 return null;
1332             };
1333             Function<String, String> nameProducer = (l) ->
1334                     "log(Level." + l + ", class, method, bundle, \"" + msg + "\", thrown)";
1335             testJdkLog(logger, tester, check, nameProducer);
1336 
1337         }
1338 
1339         public void testLogp(java.lang.System.Logger logger, String className,
1340                 String methodName, Supplier<String> msg) {
1341             Checker<BackendRecord, Level> check = (res, l) -> {
1342                 checkRecord("log", res, logger.getName(), l,
1343                             logpMessage(null, className, methodName, msg),
1344                             adaptor().getCallerClassName(
1345                                     JdkLogMethodInvoker.LOGP_SUPPLIER, className),
1346                             adaptor().getCallerClassName(
1347                                     JdkLogMethodInvoker.LOGP_SUPPLIER, methodName),
1348                             null, null);
1349                 return null;
1350             };
1351             JdkLogTester tester = (x, level) -> {
1352                 JdkLogMethodInvoker.LOGP_SUPPLIER.logX(x, level,
1353                         className, methodName, msg);
1354                 return null;
1355             };
1356             Function<String, String> nameProducer = (l) ->
1357                     "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\")";
1358             testJdkLog(logger, tester, check, nameProducer);
1359         }
1360 
1361         public void testLogp(java.lang.System.Logger logger, String className,
1362                 String methodName, Throwable thrown, Supplier<String> msg) {
1363             Checker<BackendRecord, Level> check = (res, l) -> {
1364                 checkRecord("log", res, logger.getName(), l,
1365                             logpMessage(null, className, methodName, msg),
1366                             adaptor().getCallerClassName(
1367                                     JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, className),
1368                             adaptor().getCallerClassName(
1369                                     JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, methodName),
1370                             thrown, null);
1371                 return null;
1372             };
1373             JdkLogTester tester = (x, level) -> {
1374                 JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN.logX(x, level,
1375                         className, methodName, thrown, msg);
1376                 return null;
1377             };
1378             Function<String, String> nameProducer = (l) ->
1379                     "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\", thrown)";
1380             testJdkLog(logger, tester, check, nameProducer);
1381         }
1382 
1383         private void testJdkLog(java.lang.System.Logger logger,
1384                 JdkLogTester log, Checker<BackendRecord,Level> check,
1385                 Function<String, String> nameProducer) {
1386             if (restrictedTo != null) {
1387                 if (!bridgeLoggerClass.isAssignableFrom(restrictedTo)) {
1388                     if (VERBOSE) {
1389                         System.out.println("Skipping method from "
1390                             + bridgeLoggerClass);
1391                     }
1392                     return;
1393                 }
1394             }
1395             System.out.println("Testing Logger." + nameProducer.apply("*")
1396                      + " on " + logger);
1397             final BackendAdaptor adaptor = adaptor();
1398             for (Level loggerLevel : LEVELS) {
1399                 adaptor.setLevel(logger, loggerLevel);
1400                 for (Level l : LEVELS) {
1401                     check(logger, () -> log.apply(bridgeLoggerClass.cast(logger), l),
1402                           check, () -> adaptor.isLoggable(logger, l),
1403                           () -> adaptor.shouldBeLoggable(l, loggerLevel),
1404                           l, loggerLevel, nameProducer.apply(l.toString()));
1405                 }
1406             }
1407         }
1408 
1409         private void testSpiLog(java.lang.System.Logger logger,
1410                 SpiLogTester log, Checker<BackendRecord, java.lang.System.Logger.Level> check,
1411                 Function<String, String> nameProducer) {
1412             System.out.println("Testing System.Logger." + nameProducer.apply("*")
1413                      + " on " + logger);
1414             final BackendAdaptor adaptor = adaptor();
1415             for (java.lang.System.Logger.Level loggerLevel : java.lang.System.Logger.Level.values()) {
1416 
1417                 adaptor.setLevel(logger, loggerLevel);
1418                 for (java.lang.System.Logger.Level l : java.lang.System.Logger.Level.values()) {
1419                     check(logger, () -> log.apply(logger, l),
1420                           check, () -> logger.isLoggable(l),
1421                           () -> adaptor.shouldBeLoggable(l, loggerLevel),
1422                           l, loggerLevel, nameProducer.apply(l.toString()));
1423                 }
1424             }
1425         }
1426 
1427         private void test(String args, Levels level, java.lang.System.Logger logger,
1428                 Runnable test, Checker<BackendRecord, Level> check) {
1429             if (restrictedTo != null) {
1430                 if (!level.definingClass.isAssignableFrom(restrictedTo)) {
1431                     if (VERBOSE) {
1432                         System.out.println("Skipping method from "
1433                             + level.definingClass);
1434                     }
1435                     return;
1436                 }
1437             }
1438             String method = args.contains("bundle") ? "logrb" : "log";
1439             System.out.println("Testing Logger."
1440                     + method + "(Level." + level.platformLevel
1441                     + ", "+ args + ")" + " on " + logger);
1442             final BackendAdaptor adaptor = adaptor();
1443             for (Level loggerLevel : LEVELS) {
1444                 adaptor.setLevel(logger, loggerLevel);
1445                 check(logger, test, check,
1446                         () -> level.isEnabled(logger),
1447                         () -> adaptor.shouldBeLoggable(level, loggerLevel),
1448                         level.platformLevel, loggerLevel, level.method);
1449             }
1450         }
1451 
1452         private <L> void check(java.lang.System.Logger logger,
1453                 Runnable test, Checker<BackendRecord,L> check,
1454                 BooleanSupplier checkLevelEnabled,
1455                 BooleanSupplier shouldBeLoggable,
1456                 L logLevel, L loggerLevel, String logMethod) {
1457             final BackendAdaptor adaptor = adaptor();
1458             adaptor.resetBackendRecords();
1459             test.run();
1460             final List<BackendRecord> records = adaptor.getBackendRecords();
1461             if (shouldBeLoggable.getAsBoolean()) {
1462                 if (!checkLevelEnabled.getAsBoolean()) {
1463                     throw new RuntimeException("Logger is not enabled for "
1464                             + logMethod
1465                             + " although logger level is " + loggerLevel);
1466                 }
1467                 if (records.size() != 1) {
1468                     throw new RuntimeException(loggerLevel + " [" +
1469                             logLevel + "] : Unexpected record sizes: "
1470                         + records.toString());
1471                 }
1472                 BackendRecord res = records.get(0);
1473                 check.apply(res, logLevel);
1474             } else {
1475                 if (checkLevelEnabled.getAsBoolean()) {
1476                     throw new RuntimeException("Logger is enabled for "
1477                             + logMethod
1478                             + " although logger level is " + loggerLevel);
1479                 }
1480                 if (!records.isEmpty()) {
1481                     throw new RuntimeException(loggerLevel + " [" +
1482                             logLevel + "] : Unexpected record sizes: "
1483                         + records.toString());
1484                 }
1485             }
1486         }
1487     }
1488 
1489     public static class JULBackendTester extends BackendTester<LogRecord>{
1490 
1491         public JULBackendTester(boolean isSystem) {
1492             this(isSystem,null,null);
1493         }
1494         public JULBackendTester(boolean isSystem, ResourceBundle localized) {
1495             this(isSystem,null,localized);
1496         }
1497         public JULBackendTester(boolean isSystem,
1498                 Class<? extends java.lang.System.Logger> restrictedTo) {
1499             this(isSystem, restrictedTo, null);
1500         }
1501         public JULBackendTester(boolean isSystem,
1502                 Class<? extends java.lang.System.Logger> restrictedTo,
1503                 ResourceBundle localized) {
1504             super(isSystem, restrictedTo, localized);
1505         }
1506 
1507         Logger getBackendLogger(String name) {
1508             if (isSystem) {
1509                 return LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
1510                         LogManager.getLogManager(), name, Thread.class.getModule());
1511             } else {
1512                 return Logger.getLogger(name);
1513             }
1514         }
1515 
1516         class JULBackendAdaptor extends BackendAdaptor {
1517             @Override
1518             public String getLoggerName(LogRecord res) {
1519                 return res.getLoggerName();
1520             }
1521             @Override
1522             public Level getLevel(LogRecord res) {
1523                 return Level.valueOf(res.getLevel().getName());
1524             }
1525             @Override
1526             public String getMessage(LogRecord res) {
1527                 return res.getMessage();
1528             }
1529             @Override
1530             public String getSourceClassName(LogRecord res) {
1531                 return res.getSourceClassName();
1532             }
1533             @Override
1534             public String getSourceMethodName(LogRecord res) {
1535                 return res.getSourceMethodName();
1536             }
1537             @Override
1538             public Throwable getThrown(LogRecord res) {
1539                 return res.getThrown();
1540             }
1541             @Override
1542             public ResourceBundle getResourceBundle(LogRecord res) {
1543                 return res.getResourceBundle();
1544             }
1545             @Override
1546             public void setLevel(java.lang.System.Logger logger, Level level) {
1547                 Logger backend = getBackendLogger(logger.getName());
1548                 backend.setLevel(java.util.logging.Level.parse(level.name()));
1549             }
1550             @Override
1551             public void setLevel(java.lang.System.Logger logger, java.lang.System.Logger.Level level) {
1552                 setLevel(logger, toJUL(level));
1553             }
1554             @Override
1555             public List<LogRecord> getBackendRecords() {
1556                 return handler.records;
1557             }
1558             @Override
1559             public void resetBackendRecords() {
1560                 handler.reset();
1561             }
1562             @Override
1563             public Level getMappedLevel(Object level) {
1564                 if (level instanceof java.lang.System.Logger.Level) {
1565                     return toJUL((java.lang.System.Logger.Level)level);
1566                 }
1567                 return (Level)level;
1568             }
1569         }
1570 
1571         final JULBackendAdaptor julAdaptor = new JULBackendAdaptor();
1572 
1573         @Override
1574         BackendAdaptor adaptor() {
1575             return julAdaptor;
1576         }
1577 
1578     }
1579 
1580     public abstract static class BackendTesterFactory {
1581         public abstract BackendTester createBackendTester(boolean isSystem);
1582         public abstract  BackendTester createBackendTester(boolean isSystem,
1583                 Class<? extends java.lang.System.Logger> restrictedTo);
1584         public abstract  BackendTester createBackendTester(boolean isSystem,
1585                 Class<? extends java.lang.System.Logger> restrictedTo,
1586                 ResourceBundle bundle);
1587         public abstract  BackendTester createBackendTester(boolean isSystem,
1588                 ResourceBundle bundle);
1589     }
1590 
1591     public static class JULBackendTesterFactory extends BackendTesterFactory {
1592 
1593         @Override
1594         public BackendTester createBackendTester(boolean isSystem) {
1595             return new JULBackendTester(isSystem);
1596         }
1597 
1598         @Override
1599         public BackendTester createBackendTester(boolean isSystem,
1600                 Class<? extends java.lang.System.Logger> restrictedTo) {
1601             return new JULBackendTester(isSystem, restrictedTo);
1602         }
1603 
1604         @Override
1605         public BackendTester createBackendTester(boolean isSystem,
1606                 Class<? extends java.lang.System.Logger> restrictedTo,
1607                 ResourceBundle bundle) {
1608             return new JULBackendTester(isSystem, restrictedTo, bundle);
1609         }
1610 
1611         @Override
1612         public BackendTester createBackendTester(boolean isSystem,
1613                 ResourceBundle bundle) {
1614             return new JULBackendTester(isSystem, bundle);
1615         }
1616     }
1617 
1618     public static class CustomLoggerFinder extends LoggerFinder {
1619 
1620         static enum CustomLevel { OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL };
1621         static CustomLevel[] customLevelMap = { CustomLevel.ALL,
1622             CustomLevel.TRACE, CustomLevel.DEBUG, CustomLevel.INFO,
1623             CustomLevel.WARN, CustomLevel.ERROR, CustomLevel.OFF
1624         };
1625         static class CustomLogRecord {
1626             public final CustomLevel logLevel;
1627             public final java.lang.System.Logger logger;
1628             public final String msg;
1629             public final Object[] params;
1630             public final Throwable thrown;
1631             public final ResourceBundle bundle;
1632 
1633             CustomLogRecord(java.lang.System.Logger producer,
1634                     CustomLevel level, String msg) {
1635                 this(producer, level, msg, (ResourceBundle)null, (Throwable)null, (Object[])null);
1636             }
1637 
1638             CustomLogRecord(java.lang.System.Logger producer,
1639                     CustomLevel level, String msg, ResourceBundle bundle,
1640                     Throwable thrown, Object... params) {
1641                 this.logger   = producer;
1642                 this.logLevel = level;
1643                 this.msg = msg;
1644                 this.params = params;
1645                 this.thrown = thrown;
1646                 this.bundle = bundle;
1647             }
1648         }
1649 
1650         static final List<CustomLogRecord>  records =
1651                 Collections.synchronizedList(new ArrayList<>());
1652 
1653         static class CustomLogger implements java.lang.System.Logger {
1654 
1655             final String name;
1656             volatile CustomLevel level;
1657             CustomLogger(String name) {
1658                 this.name = name;
1659                 this.level = CustomLevel.INFO;
1660             }
1661 
1662             @Override
1663             public String getName() {
1664                 return name;
1665             }
1666 
1667             public void setLevel(CustomLevel level) {
1668                 this.level = level;
1669             }
1670 
1671 
1672             @Override
1673             public boolean isLoggable(java.lang.System.Logger.Level level) {
1674 
1675                 return this.level != CustomLevel.OFF && this.level.ordinal()
1676                         >= customLevelMap[level.ordinal()].ordinal();
1677             }
1678 
1679             @Override
1680             public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
1681                 if (isLoggable(level)) {
1682                     records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()],
1683                               key, bundle, thrown));
1684                 }
1685             }
1686 
1687             @Override
1688             public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String format, Object... params) {
1689                 if (isLoggable(level)) {
1690                     records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()],
1691                               format, bundle, null, params));
1692                 }
1693             }
1694 
1695         }
1696 
1697         final Map<String, java.lang.System.Logger> applicationLoggers =
1698                 Collections.synchronizedMap(new HashMap<>());
1699         final Map<String, java.lang.System.Logger> systemLoggers =
1700                 Collections.synchronizedMap(new HashMap<>());
1701 
1702         @Override
1703         public java.lang.System.Logger getLogger(String name, Module caller) {
1704             ClassLoader callerLoader = caller.getClassLoader();
1705             if (callerLoader == null) {
1706                 systemLoggers.putIfAbsent(name, new CustomLogger(name));
1707                 return systemLoggers.get(name);
1708             } else {
1709                 applicationLoggers.putIfAbsent(name, new CustomLogger(name));
1710                 return applicationLoggers.get(name);
1711             }
1712         }
1713 
1714         CustomLevel fromJul(Level level) {
1715             if (level.intValue() == Level.OFF.intValue()) {
1716                 return CustomLevel.OFF;
1717             } else if (level.intValue() > Level.SEVERE.intValue()) {
1718                 return CustomLevel.ERROR;
1719             } else if (level.intValue() > Level.WARNING.intValue()) {
1720                 return CustomLevel.ERROR;
1721             } else if (level.intValue() > Level.INFO.intValue()) {
1722                 return CustomLevel.WARN;
1723             } else if (level.intValue() > Level.CONFIG.intValue()) {
1724                 return CustomLevel.INFO;
1725             } else if (level.intValue() > Level.FINER.intValue()) {
1726                 return CustomLevel.DEBUG;
1727             } else if (level.intValue() > Level.FINEST.intValue()) {
1728                 return CustomLevel.TRACE;
1729             } else if (level.intValue() == Level.ALL.intValue()) {
1730                 return CustomLevel.ALL;
1731             } else {
1732                 return CustomLevel.TRACE;
1733             }
1734         }
1735 
1736         Level toJul(CustomLevel level) {
1737             switch(level) {
1738                 case OFF: return Level.OFF;
1739                 case FATAL: return Level.SEVERE;
1740                 case ERROR: return Level.SEVERE;
1741                 case WARN: return Level.WARNING;
1742                 case INFO: return Level.INFO;
1743                 case DEBUG: return Level.FINE;
1744                 case TRACE: return Level.FINER;
1745                 case ALL: return Level.ALL;
1746                 default: throw new InternalError("No such level: "+level);
1747             }
1748         }
1749 
1750     }
1751 
1752     public static class CustomBackendTester extends
1753             BackendTester<CustomLoggerFinder.CustomLogRecord> {
1754 
1755         public final CustomLoggerFinder provider;
1756 
1757         public CustomBackendTester(boolean isSystem) {
1758             this(isSystem, null, null);
1759         }
1760 
1761         public CustomBackendTester(boolean isSystem,
1762                 Class<? extends java.lang.System.Logger> restrictedTo) {
1763             this(isSystem, restrictedTo, null);
1764         }
1765 
1766         public CustomBackendTester(boolean isSystem,
1767                 ResourceBundle localized) {
1768             this(isSystem, null, localized);
1769         }
1770 
1771         public CustomBackendTester(boolean isSystem,
1772                 Class<? extends java.lang.System.Logger> restrictedTo,
1773                 ResourceBundle localized) {
1774             super(isSystem, restrictedTo, localized);
1775             provider = (CustomLoggerFinder)java.lang.System.LoggerFinder.getLoggerFinder();
1776         }
1777 
1778         @Override
1779         public java.lang.System.Logger convert(java.lang.System.Logger logger) {
1780             if (restrictedTo != null && restrictedTo.isInstance(logger)) {
1781                 return logger;
1782             } else if (restrictedTo == jdkLoggerClass) {
1783                 return logger;
1784             } else {
1785                 return java.lang.System.Logger.class.cast(
1786                         sun.util.logging.PlatformLogger.Bridge.convert(logger));
1787             }
1788         }
1789 
1790         class CustomBackendAdaptor extends BackendAdaptor {
1791 
1792             @Override
1793             public String getLoggerName(CustomLoggerFinder.CustomLogRecord res) {
1794                 return res.logger.getName();
1795             }
1796 
1797             @Override
1798             public CustomLoggerFinder.CustomLevel getLevel(CustomLoggerFinder.CustomLogRecord res) {
1799                 return res.logLevel;
1800             }
1801 
1802             @Override
1803             public String getMessage(CustomLoggerFinder.CustomLogRecord res) {
1804                 return res.msg;
1805             }
1806 
1807             @Override // we don't support source class name in our custom provider implementation
1808             public String getSourceClassName(CustomLoggerFinder.CustomLogRecord res) {
1809                 return null;
1810             }
1811 
1812             @Override // we don't support source method name in our custom provider implementation
1813             public String getSourceMethodName(CustomLoggerFinder.CustomLogRecord res) {
1814                 return null;
1815             }
1816 
1817             @Override
1818             public Throwable getThrown(CustomLoggerFinder.CustomLogRecord res) {
1819                 return res.thrown;
1820             }
1821 
1822             @Override
1823             public ResourceBundle getResourceBundle(CustomLoggerFinder.CustomLogRecord res) {
1824                 return res.bundle;
1825             }
1826 
1827             @Override
1828             public void setLevel(java.lang.System.Logger logger, Level level) {
1829                 final CustomLoggerFinder.CustomLogger l =
1830                         (CustomLoggerFinder.CustomLogger)
1831                         (isSystem ? provider.getLogger(logger.getName(), Thread.class.getModule()) :
1832                         provider.getLogger(logger.getName(), LoggerFinderBackendTest.class.getModule()));
1833                 l.setLevel(provider.fromJul(level));
1834             }
1835             @Override
1836             public void setLevel(java.lang.System.Logger logger,
1837                     java.lang.System.Logger.Level level) {
1838                 setLevel(logger, toJUL(level));
1839             }
1840 
1841             CustomLoggerFinder.CustomLevel getLevel(java.lang.System.Logger logger) {
1842                 final CustomLoggerFinder.CustomLogger l =
1843                         (CustomLoggerFinder.CustomLogger)
1844                         (isSystem ? provider.getLogger(logger.getName(), Thread.class.getModule()) :
1845                         provider.getLogger(logger.getName(), LoggerFinderBackendTest.class.getModule()));
1846                 return l.level;
1847             }
1848 
1849             @Override
1850             public List<CustomLoggerFinder.CustomLogRecord> getBackendRecords() {
1851                 return CustomLoggerFinder.records;
1852             }
1853 
1854             @Override
1855             public void resetBackendRecords() {
1856                 CustomLoggerFinder.records.clear();
1857             }
1858 
1859             @Override
1860             public boolean shouldBeLoggable(Levels level, Level loggerLevel) {
1861                 return loggerLevel != Level.OFF &&
1862                        fromLevels(level).ordinal() <= provider.fromJul(loggerLevel).ordinal();
1863             }
1864 
1865             @Override
1866             public boolean isLoggable(java.lang.System.Logger logger, Level l) {
1867                 return super.isLoggable(logger, l);
1868             }
1869 
1870             @Override
1871             public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) {
1872                 return loggerLevel != Level.OFF &&
1873                         provider.fromJul(logLevel).ordinal() <= provider.fromJul(loggerLevel).ordinal();
1874             }
1875 
1876             @Override // we don't support source class name in our custom provider implementation
1877             public String getCallerClassName(Levels level, String clazz) {
1878                 return null;
1879             }
1880 
1881             @Override // we don't support source method name in our custom provider implementation
1882             public String getCallerMethodName(Levels level, String method) {
1883                 return null;
1884             }
1885 
1886             @Override // we don't support source class name in our custom provider implementation
1887             public String getCallerClassName(MethodInvoker<?,?> logMethod, String clazz) {
1888                 return null;
1889             }
1890 
1891             @Override // we don't support source method name in our custom provider implementation
1892             public String getCallerMethodName(MethodInvoker<?,?> logMethod, String method) {
1893                 return null;
1894             }
1895 
1896             @Override
1897             public CustomLoggerFinder.CustomLevel getMappedLevel(Object level) {
1898                 if (level instanceof java.lang.System.Logger.Level) {
1899                     final int index = ((java.lang.System.Logger.Level)level).ordinal();
1900                     return CustomLoggerFinder.customLevelMap[index];
1901                 } else if (level instanceof Level) {
1902                     return provider.fromJul((Level)level);
1903                 }
1904                 return (CustomLoggerFinder.CustomLevel) level;
1905             }
1906 
1907             CustomLoggerFinder.CustomLevel fromLevels(Levels level) {
1908                 switch(level) {
1909                     case SEVERE:
1910                         return CustomLoggerFinder.CustomLevel.ERROR;
1911                     case WARNING:
1912                         return CustomLoggerFinder.CustomLevel.WARN;
1913                     case INFO:
1914                         return CustomLoggerFinder.CustomLevel.INFO;
1915                     case CONFIG: case FINE:
1916                         return CustomLoggerFinder.CustomLevel.DEBUG;
1917                     case FINER:  case FINEST:
1918                         return CustomLoggerFinder.CustomLevel.TRACE;
1919                 }
1920                 throw new InternalError("No such level "+level);
1921             }
1922 
1923         }
1924 
1925         @Override
1926         BackendAdaptor adaptor() {
1927             return new CustomBackendAdaptor();
1928         }
1929 
1930     }
1931 
1932     public static class CustomBackendTesterFactory extends BackendTesterFactory {
1933 
1934         @Override
1935         public BackendTester createBackendTester(boolean isSystem) {
1936             return new CustomBackendTester(isSystem);
1937         }
1938 
1939         @Override
1940         public BackendTester createBackendTester(boolean isSystem,
1941                 Class<? extends java.lang.System.Logger> restrictedTo) {
1942             return new CustomBackendTester(isSystem, restrictedTo);
1943         }
1944 
1945         @Override
1946         public BackendTester createBackendTester(boolean isSystem,
1947                 Class<? extends java.lang.System.Logger> restrictedTo,
1948                 ResourceBundle bundle) {
1949             return new CustomBackendTester(isSystem, restrictedTo, bundle);
1950         }
1951 
1952         @Override
1953         public BackendTester createBackendTester(boolean isSystem,
1954                 ResourceBundle bundle) {
1955             return new CustomBackendTester(isSystem, bundle);
1956         }
1957     }
1958 
1959     static final Method getLazyLogger;
1960     static final Method accessLoggerFinder;
1961     static {
1962         // jdk.internal.logger.LazyLoggers.getLazyLogger(name, caller);
1963         try {
1964             Class<?> lazyLoggers = jdk.internal.logger.LazyLoggers.class;
1965             getLazyLogger = lazyLoggers.getMethod("getLazyLogger",
1966                     String.class, Module.class);
1967             getLazyLogger.setAccessible(true);
1968             Class<?> loggerFinderLoader =
1969                     Class.forName("java.lang.System$LoggerFinder");
1970             accessLoggerFinder = loggerFinderLoader.getDeclaredMethod("accessProvider");
1971             accessLoggerFinder.setAccessible(true);
1972         } catch (Throwable ex) {
1973             throw new ExceptionInInitializerError(ex);
1974         }
1975     }
1976 
1977     static java.lang.System.Logger getSystemLogger(String name, Module caller) throws Exception {
1978         try {
1979             return java.lang.System.Logger.class.cast(getLazyLogger.invoke(null, name, caller));
1980         } catch (InvocationTargetException x) {
1981             Throwable t = x.getTargetException();
1982             if (t instanceof Exception) {
1983                 throw (Exception)t;
1984             } else {
1985                 throw (Error)t;
1986             }
1987         }
1988     }
1989     static java.lang.System.Logger getSystemLogger(String name,
1990             ResourceBundle bundle, Module caller) throws Exception {
1991         try {
1992             LoggerFinder provider = LoggerFinder.class.cast(accessLoggerFinder.invoke(null));
1993             return provider.getLocalizedLogger(name, bundle, caller);
1994         } catch (InvocationTargetException x) {
1995             Throwable t = x.getTargetException();
1996             if (t instanceof Exception) {
1997                 throw (Exception)t;
1998             } else {
1999                 throw (Error)t;
2000             }
2001         }
2002     }
2003 
2004     // Change this to 'true' to get more traces...
2005     public static boolean verbose = false;
2006 
2007     public static void main(String[] argv) throws Exception {
2008 
2009         final AtomicInteger nb = new AtomicInteger(0);
2010         final boolean hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
2011         System.out.println(ClassLoader.getSystemClassLoader());
2012         final BackendTesterFactory factory;
2013         if (java.lang.System.LoggerFinder.getLoggerFinder() instanceof CustomLoggerFinder) {
2014             if (hidesProvider) {
2015                 System.err.println("Custom backend "
2016                         + java.lang.System.LoggerFinder.getLoggerFinder()
2017                         + " should have been hidden!");
2018                 throw new RuntimeException(
2019                         "Custom backend should have been hidden: "
2020                         + "check value of java.system.class.loader property");
2021             }
2022             System.out.println("Using custom backend");
2023             factory = new CustomBackendTesterFactory();
2024         } else {
2025             if (!hidesProvider) {
2026                 System.err.println("Default JUL backend "
2027                         + java.lang.System.LoggerFinder.getLoggerFinder()
2028                         + " should have been hidden!");
2029                 throw new RuntimeException(
2030                         "Default JUL backend should have been hidden: "
2031                         + "check value of java.system.class.loader property");
2032             }
2033             System.out.println("Using JUL backend");
2034             factory = new JULBackendTesterFactory();
2035         }
2036 
2037         testBackend(nb, factory);
2038     }
2039 
2040     public static void testBackend(AtomicInteger nb, BackendTesterFactory factory) throws Exception {
2041 
2042         // Tests all level specifics methods with loggers configured with
2043         // all possible levels and loggers obtained with all possible
2044         // entry points from LoggerFactory and JdkLoggerFactory, with
2045         // JUL as backend.
2046 
2047         // Test a simple application logger with JUL backend
2048         final BackendTester tester = factory.createBackendTester(false);
2049         final java.lang.System.Logger logger =
2050                 java.lang.System.LoggerFinder.getLoggerFinder()
2051                         .getLogger("foo", LoggerFinderBackendTest.class.getModule());
2052 
2053         testLogger(tester, logger, nb);
2054 
2055         // Test a simple system logger with JUL backend
2056         final java.lang.System.Logger system =
2057                 java.lang.System.LoggerFinder.getLoggerFinder()
2058                         .getLogger("bar", Thread.class.getModule());
2059         final BackendTester systemTester = factory.createBackendTester(true);
2060         testLogger(systemTester, system, nb);
2061 
2062         // Test a localized application logger with null resource bundle and
2063         // JUL backend
2064         final java.lang.System.Logger noBundleLogger =
2065                 java.lang.System.LoggerFinder.getLoggerFinder()
2066                         .getLocalizedLogger("baz", null, LoggerFinderBackendTest.class.getModule());
2067         final BackendTester noBundleTester =
2068                 factory.createBackendTester(false, spiLoggerClass);
2069         testLogger(noBundleTester, noBundleLogger, nb);
2070 
2071         // Test a localized system logger with null resource bundle and JUL
2072         // backend
2073         final java.lang.System.Logger noBundleSysLogger =
2074                 java.lang.System.LoggerFinder.getLoggerFinder()
2075                         .getLocalizedLogger("oof", null, Thread.class.getModule());
2076         final BackendTester noBundleSysTester =
2077                 factory.createBackendTester(true, spiLoggerClass);
2078         testLogger(noBundleSysTester, noBundleSysLogger, nb);
2079 
2080         // Test a localized application logger with null resource bundle and
2081         // JUL backend
2082         try {
2083             System.getLogger("baz", null);
2084             throw new RuntimeException("Expected NullPointerException not thrown");
2085         } catch (NullPointerException x) {
2086             System.out.println("System.Loggers.getLogger(\"baz\", null): got expected " + x);
2087         }
2088         final java.lang.System.Logger noBundleExtensionLogger =
2089                 getSystemLogger("baz", null, LoggerFinderBackendTest.class.getModule());
2090         final BackendTester noBundleExtensionTester =
2091                 factory.createBackendTester(false, jdkLoggerClass);
2092         testLogger(noBundleExtensionTester, noBundleExtensionLogger, nb);
2093 
2094         // Test a simple system logger with JUL backend
2095         final java.lang.System.Logger sysExtensionLogger =
2096                 getSystemLogger("oof", Thread.class.getModule());
2097         final BackendTester sysExtensionTester =
2098                 factory.createBackendTester(true, jdkLoggerClass);
2099         testLogger(sysExtensionTester, sysExtensionLogger, nb);
2100 
2101         // Test a localized system logger with null resource bundle and JUL
2102         // backend
2103         final java.lang.System.Logger noBundleSysExtensionLogger =
2104                 getSystemLogger("oof", null, Thread.class.getModule());
2105         final BackendTester noBundleSysExtensionTester =
2106                 factory.createBackendTester(true, jdkLoggerClass);
2107         testLogger(noBundleSysExtensionTester, noBundleSysExtensionLogger, nb);
2108 
2109         // Test a localized application logger converted to JDK with null
2110         // resource bundle and JUL backend
2111         final java.lang.System.Logger noBundleConvertedLogger =
2112                 (java.lang.System.Logger)
2113                 sun.util.logging.PlatformLogger.Bridge.convert(noBundleLogger);
2114         final BackendTester noBundleJdkTester = factory.createBackendTester(false);
2115         testLogger(noBundleJdkTester, noBundleConvertedLogger, nb);
2116 
2117         // Test a localized system logger converted to JDK with null resource
2118         // bundle and JUL backend
2119         final java.lang.System.Logger noBundleConvertedSysLogger =
2120                 (java.lang.System.Logger)
2121                 sun.util.logging.PlatformLogger.Bridge.convert(noBundleSysLogger);
2122         final BackendTester noBundleJdkSysTester = factory.createBackendTester(true);
2123         testLogger(noBundleJdkSysTester, noBundleConvertedSysLogger, nb);
2124 
2125         // Test a localized application logger with resource bundle and JUL
2126         // backend
2127         final ResourceBundle bundle =
2128                 ResourceBundle.getBundle(ResourceBundeLocalized.class.getName());
2129         final java.lang.System.Logger bundleLogger =
2130                 java.lang.System.LoggerFinder.getLoggerFinder()
2131                         .getLocalizedLogger("toto", bundle, LoggerFinderBackendTest.class.getModule());
2132         final BackendTester bundleTester =
2133                 factory.createBackendTester(false, spiLoggerClass, bundle);
2134         testLogger(bundleTester, bundleLogger, nb);
2135 
2136         // Test a localized system logger with resource bundle and JUL backend
2137         final java.lang.System.Logger bundleSysLogger =
2138                 java.lang.System.LoggerFinder.getLoggerFinder()
2139                         .getLocalizedLogger("titi", bundle, Thread.class.getModule());
2140         final BackendTester bundleSysTester =
2141                 factory.createBackendTester(true, spiLoggerClass, bundle);
2142         testLogger(bundleSysTester, bundleSysLogger, nb);
2143 
2144         // Test a localized Jdk application logger with resource bundle and JUL
2145         // backend
2146         final java.lang.System.Logger bundleExtensionLogger =
2147                 System.getLogger("tita", bundle);
2148         final BackendTester bundleExtensionTester =
2149                 factory.createBackendTester(false, jdkLoggerClass, bundle);
2150         testLogger(bundleExtensionTester, bundleExtensionLogger, nb);
2151 
2152         // Test a localized Jdk system logger with resource bundle and JUL
2153         // backend
2154         final java.lang.System.Logger bundleExtensionSysLogger =
2155                 getSystemLogger("titu", bundle, Thread.class.getModule());
2156         final BackendTester bundleExtensionSysTester =
2157                 factory.createBackendTester(true, jdkLoggerClass, bundle);
2158         testLogger(bundleExtensionSysTester, bundleExtensionSysLogger, nb);
2159 
2160         // Test a localized application logger converted to JDK with resource
2161         // bundle and JUL backend
2162         final BackendTester bundleJdkTester =
2163                 factory.createBackendTester(false, bundle);
2164         final java.lang.System.Logger bundleConvertedLogger =
2165                 (java.lang.System.Logger)
2166                 sun.util.logging.PlatformLogger.Bridge.convert(bundleLogger);
2167         testLogger(bundleJdkTester, bundleConvertedLogger, nb);
2168 
2169         // Test a localized Jdk system logger converted to JDK with resource
2170         // bundle and JUL backend
2171         final BackendTester bundleJdkSysTester =
2172                 factory.createBackendTester(true, bundle);
2173         final java.lang.System.Logger bundleConvertedSysLogger =
2174                 (java.lang.System.Logger)
2175                 sun.util.logging.PlatformLogger.Bridge.convert(bundleSysLogger);
2176         testLogger(bundleJdkSysTester, bundleConvertedSysLogger, nb);
2177 
2178         // Now need to add tests for all the log/logp/logrb methods...
2179 
2180     }
2181 
2182     private static class FooObj {
2183         final String s;
2184         FooObj(String s) {
2185             this.s = s;
2186         }
2187 
2188         @Override
2189         public String toString() {
2190             return super.toString() +": "+s;
2191         }
2192 
2193     }
2194 
2195     public static void testLogger(BackendTester tester,
2196             java.lang.System.Logger spiLogger, AtomicInteger nb) {
2197 
2198         // Test all level-specific method forms:
2199         // fatal(...) error(...) severe(...) etc...
2200         java.lang.System.Logger jdkLogger = tester.convert(spiLogger);
2201         for (Levels l : Levels.values()) {
2202             java.lang.System.Logger logger =
2203                     l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
2204             tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
2205                     + nb.incrementAndGet());
2206             tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
2207                     + nb.incrementAndGet(),
2208                     bundleParam);
2209             final int nbb = nb.incrementAndGet();
2210             tester.testLevel(l, logger, () -> l.method + "[" + logger.getName()
2211                     + "]-" + nbb);
2212         }
2213         for (Levels l : Levels.values()) {
2214             java.lang.System.Logger logger =
2215                     l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
2216             tester.testLevel(l, logger,
2217                     l.method + "[" + logger.getName()+ "]({0},{1})-"
2218                     + nb.incrementAndGet(),
2219                     "One", "Two");
2220             tester.testLevel(l, logger,
2221                     l.method + "[" + logger.getName()+ "]({0},{1})-"
2222                     + nb.incrementAndGet(),
2223                     bundleParam, "One", "Two");
2224         }
2225         final Throwable thrown = new RuntimeException("Test");
2226         for (Levels l : Levels.values()) {
2227             java.lang.System.Logger logger =
2228                     l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
2229             tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
2230                     + nb.incrementAndGet(),
2231                     thrown);
2232             tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
2233                     + nb.incrementAndGet(),
2234                     bundleParam, thrown);
2235             final int nbb = nb.incrementAndGet();
2236             tester.testLevel(l, logger, ()->l.method + "[" + logger.getName()+ "]-"
2237                     + nbb, thrown);
2238         }
2239 
2240         java.lang.System.Logger logger = jdkLogger;
2241 
2242          // test System.Logger methods
2243        tester.testSpiLog(logger, "[" + logger.getName()+ "]-"
2244                 + nb.incrementAndGet());
2245         tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-"
2246                 + nb.incrementAndGet());
2247         tester.testSpiLog(logger, "[" + logger.getName()+ "]-({0},{1})"
2248                 + nb.incrementAndGet(), "One", "Two");
2249         tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})"
2250                 + nb.incrementAndGet(), "One", "Two");
2251         tester.testSpiLog(logger, "[" + logger.getName()+ "]-"
2252                 + nb.incrementAndGet(), thrown);
2253         tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-"
2254                 + nb.incrementAndGet(), thrown);
2255         final int nbb01 = nb.incrementAndGet();
2256         tester.testSpiLog(logger, () -> "[" + logger.getName()+ "]-" + nbb01);
2257         final int nbb02 = nb.incrementAndGet();
2258         tester.testSpiLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb02);
2259         final int nbb03 = nb.incrementAndGet();
2260         tester.testSpiLog(logger, new FooObj("[" + logger.getName()+ "]-" + nbb03));
2261 
2262         // Test all log method forms:
2263         // jdk.internal.logging.Logger.log(...)
2264         tester.testLog(logger, "[" + logger.getName()+ "]-"
2265                 + nb.incrementAndGet());
2266         tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-"
2267                 + nb.incrementAndGet());
2268         tester.testLog(logger, "[" + logger.getName()+ "]-({0},{1})"
2269                 + nb.incrementAndGet(), "One", "Two");
2270         tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})"
2271                 + nb.incrementAndGet(), "One", "Two");
2272         tester.testLog(logger, "[" + logger.getName()+ "]-"
2273                 + nb.incrementAndGet(), thrown);
2274         tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-"
2275                 + nb.incrementAndGet(), thrown);
2276         final int nbb1 = nb.incrementAndGet();
2277         tester.testLog(logger, () -> "[" + logger.getName()+ "]-" + nbb1);
2278         final int nbb2 = nb.incrementAndGet();
2279         tester.testLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb2);
2280 
2281         // Test all logp method forms
2282         // jdk.internal.logging.Logger.logp(...)
2283         tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
2284                 "method" + nb.incrementAndGet(),
2285                 "[" + logger.getName()+ "]-"
2286                 + nb.incrementAndGet());
2287         tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
2288                 "method" + nb.incrementAndGet(), bundleParam,
2289                 "[" + logger.getName()+ "]-"
2290                 + nb.incrementAndGet());
2291         tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
2292                 "method" + nb.incrementAndGet(),
2293                 "[" + logger.getName()+ "]-({0},{1})"
2294                 + nb.incrementAndGet(), "One", "Two");
2295         tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
2296                 "method" + nb.incrementAndGet(), bundleParam,
2297                 "[" + logger.getName()+ "]-({0},{1})"
2298                 + nb.incrementAndGet(), "One", "Two");
2299         tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
2300                 "method" + nb.incrementAndGet(),
2301                 "[" + logger.getName()+ "]-"
2302                 + nb.incrementAndGet(), thrown);
2303         tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
2304                 "method" + nb.incrementAndGet(), bundleParam,
2305                 "[" + logger.getName()+ "]-"
2306                 + nb.incrementAndGet(), thrown);
2307         final int nbb3 = nb.incrementAndGet();
2308         tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
2309                 "method" + nb.incrementAndGet(),
2310                 () -> "[" + logger.getName()+ "]-" + nbb3);
2311         final int nbb4 = nb.incrementAndGet();
2312         tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
2313                 "method" + nb.incrementAndGet(),
2314                 thrown, () -> "[" + logger.getName()+ "]-" + nbb4);
2315     }
2316 
2317 }